Consider the following code snippet:
template<class T>
std::enable_if_t<std::is_integral<T>::value, bool> func(T value) {
std::cout << "T\n";
return func(static_cast<int64_t>(value));
}
bool func(int64_t value) {
std::cout << "int64_t\n";
return true;
}
int main() {
func(1);
}
It causes infinite recursion. However, swapping the definition order of these two functions (if bool func(int64_t value) defined before template one) helps to avoid this issue.
Why is that? Does order of function overloads matter?
Does order of function overloads matter?
It doesn't change the result of overload resolution, but the result of name lookup; which happens before overload resolution.
(emphasis mine)
For a name used in global (top-level namespace) scope, outside of any function, class, or user-declared namespace, the global scope before the use of the name is examined:
That means for the invocation of func inside the templated func, only itself could be found (and be added to the overload set), the non-template version won't be considered at all.
As you have seen, if you change their declaration order, both func would be found and considered at the following overload resolution, and the non-template func is selected (as expected).
When the compiler is parsing the first function (the template) it doesn't know anything about the second overload of func. Parsing is done top-to-bottom of the source file.
That's the reason you need to have declarations of symbols before you use them. That also means just having a declaration of bool func(int64_t); at the top would solve your problem.
Does order of function overloads matter?
The order of overloads does matter in the sense that if the overload set is called before a potential overload is declared, that not-yet declared function doesn't take part in the overload resolution.
The order of overloads declared before the function call does not matter.
Related
Consider the following code snippet:
template<class T>
std::enable_if_t<std::is_integral<T>::value, bool> func(T value) {
std::cout << "T\n";
return func(static_cast<int64_t>(value));
}
bool func(int64_t value) {
std::cout << "int64_t\n";
return true;
}
int main() {
func(1);
}
It causes infinite recursion. However, swapping the definition order of these two functions (if bool func(int64_t value) defined before template one) helps to avoid this issue.
Why is that? Does order of function overloads matter?
Does order of function overloads matter?
It doesn't change the result of overload resolution, but the result of name lookup; which happens before overload resolution.
(emphasis mine)
For a name used in global (top-level namespace) scope, outside of any function, class, or user-declared namespace, the global scope before the use of the name is examined:
That means for the invocation of func inside the templated func, only itself could be found (and be added to the overload set), the non-template version won't be considered at all.
As you have seen, if you change their declaration order, both func would be found and considered at the following overload resolution, and the non-template func is selected (as expected).
When the compiler is parsing the first function (the template) it doesn't know anything about the second overload of func. Parsing is done top-to-bottom of the source file.
That's the reason you need to have declarations of symbols before you use them. That also means just having a declaration of bool func(int64_t); at the top would solve your problem.
Does order of function overloads matter?
The order of overloads does matter in the sense that if the overload set is called before a potential overload is declared, that not-yet declared function doesn't take part in the overload resolution.
The order of overloads declared before the function call does not matter.
I understand that with concepts, a constrained function (regardless how "loose" the constraint actually is) is always a better match than an unconstrained function. But is there any syntax to selectively call the unconstrained version of f() as in the sample code below? If not, would it be a good idea for compilers to warn about uncallable functions?
#include <iostream>
template <typename T> requires(true)
void f() { std::cout << "Constrained\n"; }
template <typename T>
void f() { std::cout << "NOT Constrained\n"; }
int main() {
f<int>();
}
https://godbolt.org/z/n164aTvd3
Different overloads of a function are meant to all do the same thing. They may do it in different ways or on different kinds of objects, but at a conceptual level, all overloads of a function are supposed to do the same thing.
This includes constrained functions. By putting a constrained overload in a function overload set, you are declaring that this is a valid alternative method for doing what that overload set does. As such, if the constraint matches, then that's the function that should be called. Just like for parameters of different types in regular function overloading.
If you want to explicitly call an overload hidden by a constraint, you have already done something wrong in your design. Or more specifically, if some overload is completely hidden by one or more constrained overloads, you clearly have one more overload than you actually needed.
If the constraints match, the caller should be 100% fine with getting the constrained overload. And if this isn't the case, your design has a problem.
So no, there is no mechanism to do this. Just as there's no mechanism to bypass an explicitly specialized template and use the original version if your template arguments match the specialization.
template <typename T>
int get_num(const T&)
{
return 42;
}
struct Foo
{
int i = get_num(*this);
};
int get_num(const Foo&)
{
return 23;
}
int main()
{
std::cout << Foo().i << std::endl; // MinGW64 - 42, MSVC - 23
return 0;
}
MSVC chooses non-template get_num "overload". It will successfully link even if the template is only forward-declared.
MinGW64 will choose the default template implementation. Will fail to link if there is no definition for the template.
Who is right and who is wrong? Whad does the standard say?
However this version yields the same results for both compilers... Why does it not go to infinite recursion?
template <typename T>
int get_num(const T& t)
{
std::cout << "Template version called" << std::endl;
return get_num(t);
}
struct Foo
{
int i = get_num(*this);
};
int get_num(const Foo&)
{
std::cout << "Non-Template version called" << std::endl;
return 23;
}
MSVC output:
Non-Template version called
23
MinGW64 Output:
Template version called
Non-Template version called
23
I think ADL is involved when using MSVC.
For the first sample, MinGW is correct and MSVC is incorrect: the get_num call can only consider the function template.
This is a question of overload resolution, so I'll start in clause [over]. get_num(*this) is a plain function call expression, so [over.call.func] applies:
In unqualified function calls, the name is not qualified by an -> or . operator and has the more general form of a primary-expression. The name is looked up in the context of the function call following the normal rules for name lookup in function calls. The function declarations found by that lookup constitute the set of candidate functions.
"Name lookup" is discussed in section [basic.lookup]. Paragraph 2:
A name "looked up in the context of an expression" is looked up as an unqualified name in the scope where the expression is found.
One complication here is that the get_num(*this) default member initializer expression doesn't get used by anything until the default constructor of Foo is implicitly defined by its odr-use in main. But the lookup is determined from the code location of the expression itself, no matter that it's used in the process of that implicit definition.
For the second code sample, MinGW is again correct: the apparently recursive call inside the template definition actually calls the non-template function.
This is a result of "two phase lookup" for dependent function calls, described in section [temp.dep.res]. Briefly, since the type of t depends on a template parameter, the name get_num in the expression get_num(t) is considered a dependent name. So for each instantiation of the function template, it gets two ways of finding candidates for overload resolution: the ordinary immediate way which finds the get_num function template, plus another lookup from the point of instantiation. The specialization get_num<Foo> has point of instantiation right after the main() definition, so overload resolution is able to find the non-template from the instantiation context, and the non-template wins overload resolution.
(Argument-dependent lookup is a related tangent issue to this second point. ADL applies to declarations from the instantiation context but not declarations from the definition context. But it's not directly a reason for the behaviors in either example program.)
None of this has changed significantly between C++14 and the latest draft, as far as I can see.
I accidentally find the following two templates can be overloaded(don't incur a name redefined error), which I think is counter-intuitive.
template<typename T>
void func(T) {}
template<typename T>
int func(T) {return 0;}
From cppreference.com, there is a related paragraph:
When an expression that uses type or non-type template parameters
appears in the function parameter list or in the return type, that
expression remains a part of the function template signature for the
purpose of overloading:
But the return types of those two functions don't include T. Who can explain it for me?
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.
the following two templates can be overloaded(don't incur a name redefined error), which I think is counter-intuitive.
Not really.
The two functions can't be overloaded, but the compiler just does not know their existence until the very moment of them being instantiated:
// Try invoking `func`
func(0xFF);
Now the compiler will throw an error message similar to:
error: call to 'func' is ambiguous
Consider the following code:
#include <iostream>
template<typename T>
void f(T t)
{
(void)t;
std::cout << "templated f(T)\n";
}
template<typename T>
void entry(T t)
{
f(t);
}
void f(double d)
{
(void)d;
std::cout << "normal f(double)\n";
}
int main()
{
double d = 0.0;
entry(d);
return 0;
}
Output:
templated f(T)
I find this surprising, because I thought that the plain function will be selected over any templated version. Why does this happen?
Another thing I noticed while playing around is that: if I put the normal function void f(double) before the templated void entry(T) function the code will call the normal function, basically outputting:
normal f(double)
Therefore my other question: why does the order matter in this particular example?
f is a dependent name, since it depends on t whose type is a template parameter. The name lookup rules for dependent names are given in [temp.dep.res]/1:
In resolving dependent names, names from the following sources are considered:
Declarations that are visible at the point of definition of the template.
Declarations from namespaces associated with the types of the function arguments both from the
instantiation context (14.6.4.1) and from the definition context.
In other words, normally name lookup inside a template only finds names that have been declared before the template definition (which is not that surprising, since it's the same as for non-templates). The second bullet point allows names declared after the template definition to be found, but only when ADL occurs. This won't be the case when the argument is a fundamental type such as double.
The overload for f(double) is not visible to the compiler when the entry(T) template is parsed. Therefore, it won't participate in overload resolution when the entry(T) template is instantiated. This is just an obscure rule when it comes to resolving overloads in a template instantiation context. In order for an overload to be considered, it has to already have been visible in the translation unit before the template definition was parsed.