Why Does Overload of Template and Non-Template Function with the "Same Signature" Call the Non-Template Function

Why does overload of template and non-template function with the same signature call the non-template function?

Because the second overload is not a template.

When a template function and a non-template function are both viable for resolving a function call, the non-template function is selected.

From Paragraph 13.3.3/1 of the C++ 11 Standard:

[...] Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then [...] F1 is a non-template function and F2 is a function template specialization [...]

A template function and a non-template overload of this function, which is a best match?

Since int * is not implicitly convertible to char * nor to const char *, the template function will be called. Removing the template function would result in compile time error.

Suggestion: There is great value in playing around with the compiler yourself. You can add lines like

std::cout << "template function called.\n";

into your overloads or use the debugger to do that kind of stuff. It's a great learning experience. You might also simply read some C++ books for an introduction.

Why the overload resolution below calls the non-template function?

Why the overload resolution for the call max(x, y) in the expression return max(max(x, y), z); below results in a call to the non-template function char const* max(char const*, char const*)?

When invoking this function:

template <typename T>
T const& max (T const& x, T const& y, T const& z)
{
return max (max(x, y), z);
}

T is deduced to be const char*. Therefore, this signature is instantiated:

const char* const& max (
const char* const& x,
const char* const& y,
const char* const& z
)

The function internally calls the binary version of max() with arguments of type const char*. Both the template and the non-template overload are viable for an argument of type const char*.

However, when two functions are viable for resolving the call and one of them is not a template, the non-template version is considered a best fit.

Per Paragraph 13.3.3/1 of the C++11 Standard:

Given these definitions,** a viable function F1 is defined to be a better function than another viable function
F2 if** for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

— for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,

— the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the
standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the
entity being initialized) is a better conversion sequence than the standard conversion sequence from
the return type of F2 to the destination type. [ ... ] or, if not that,

F1 is a non-template function and F2 is a function template specialization, or, if not that,

— F1 and F2 are function template specializations, and the function template for F1 is more specialized
than the template for F2 according to the partial ordering rules described in 14.5.6.2.

This explains why the non-template overload is picked.

Template specialization vs. Function overloading

Fist variant is simpler because compiler will have to collect and then choose from set of candidates containing only a single item add<int>(int, int) specialization. While second variant will cause compiler to perform some extra job by collecting a set of candidates containing two items - add<int>(int, int) and add(int, int) and choose between them using fancy function overload ranking algorithm.

Overloaded non-type template is ambiguous while non-templated function is ok

Here's what happens when you write f<0>().

  1. The compiler looks up f, and finds two function template declarations:

    template <int I>   void foo();
    template <short S> void foo();
  2. The compiler sees the explicit template argument list and attempts to substitute it into each function template declaration:

    template <int I>   void foo(); // with I = 0
    template <short S> void foo(); // with S = 0

    Substitution succeeds in both cases because 0 is an int, and can be converted to short, and the conversion is an allowed conversion in this context.

  3. After substitution, two candidate function specializations are produced. Both are viable. Overload resolution is then performed - and since the signature is identical and no tiebreaker applies, overload resolution fails and the call is ambiguous.

The point here is that the normal overload resolution rules do not apply to template arguments. The conversions for template arguments are applied in an earlier stage, before the regular overload resolution takes place.

Overload function template by return type

Your paragraph quoted is irrelevant.

There is a special rule to prevent non-template functions that differ only in the return type from being overloaded (from the standard [over.load]/2.1):

Function declarations that differ only in the return type, the exception specification, or both cannot be overloaded.

So the program is ill-formed if such declarations exist (even if the program does not call them). However, this rule neither applies to function templates, nor to template specializations synthesized for the purpose of overload resolution according to [over.load]/1.

Not all function declarations can be overloaded. Those that cannot be overloaded are specified here. A program is ill-formed if it contains two such non-overloadable declarations in the same scope. [ Note: This restriction applies to explicit declarations in a scope, and between such declarations and declarations made through a using-declaration. It does not apply to sets of functions fabricated as a result of name lookup (e.g., because of using-directives) or overload resolution (e.g., for operator functions). — end note ]

So these two templates can be well overloaded.

However, as Dean Seo said in his answer, if you try to call func, the program would be ill-formed due to the ambiguity of overload resolution.



Related Topics



Leave a reply



Submit