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
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.
After looking at the proposed resolution of #1664(proposed resolution 1664), I'm confused for the rules of a default argument of a function template, Cite the content here:
According to 8.1.5 [expr.prim.lambda] paragraph 3
The closure type is declared in the smallest block scope, class scope, or namespace scope that contains
the corresponding lambda-expression. [Note: This determines the set of namespaces and classes associated with the closure type (6.4.2 [basic.lookup.argdep]). The parameter types of a lambda-
declarator do not affect these associated namespaces and classes. —end note]
However, 17.8.1 [temp.inst] paragraph 13 says
If a function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point.
A possibility, then, is that the closure type for lambda expression in a default argument for a template function (or, presumably, a member function of a class template) is to be considered as having been declared in some block scope in the body of the fictional function template specialization.
Consider the following example:
namespace J {
inline namespace K {
template <typename T> int zap(const T &t) { foo(t); return 0; }
template <typename T> void zip(int = zap([] { })) { }
}
template <typename T> void foo(const T &) { }
}
void bar() {
J::K::zip<long>();
/*Accroding to the above wording,the invoke just like:
=> J::K::zip<long>(zap([] { }));
*/
}
If zip were not a template, argument-dependent lookup successfully resolves the lookup for foo in all implementations tested; however, there is implementation variance in the handling of the example as written.
Proposed resolution (September 2013):
Change 17.8.1 [temp.inst] paragraph 13 as follows:
If a function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point, except that the scope in which a closure type is declared (8.1.5 [expr.prim.lambda]) — and therefore its associated namespaces — remain as determined from the context of the definition for the default argument. This analysis is called default argument instantiation. The instantiated default argument is then used as the argument of f.
Note the emphasized part,if I do not misunderstand,It means if the emphasized part comment out,the foo could not be looked up by argument dependent look up because the argument [] { } which namespace is neither J nor K,assume the form within function bar like J::K::zip<long>(zap([] { }) /*default argument*/);,So accroding to [expr.prim.lambda] paragraph 3 the namespace of [] { } is at fuction bar and at that scope,there's no foo can be found,So the emphasized part is for this case that consider the namespace of [] { } within zap as the same as zap,it means the namespace of [] { } is K,Now the foo can be found in the parent namespace J by argument dependent lookup rules ,So far ,If i misundertand these rules,please correct me.the other point view is that the default argument is evaluated every time when the function called,even if the default is non-dependent.So continue consider the following code:
#include <iostream>
struct A {
};
template<typename T>
int func(T, float) { //#a
std::cout << "float" << std::endl;
return 0;
}
template<typename T>
void test(int = func(A{}, 0)) { //#1
}
template<typename T>
int func(T, int) { //#b
std::cout << "int" << std::endl;
return 0;
}
int main() {
test<A>(); //#2 transform to: test<A>(func(A{}, 0)); here,#b should be the best match
std::getchar();
}
Although the default argument func is non-dependent,However it should be determined every time when the function test is called and I test the code in some compliers.
All the version of MSVC report "int",gcc report "float",clang report "float",what's the hell?Accroding to the report of gcc or clang,It seems to the func is determined at #1and MSVC proved that the func is determined at #2. if MSVC is wrong ,that means the non-dependent default argument could be determined within #1 and it's unnecessary to determine every time when the function called,why the emphasized part need to add?(If I understand the emphasized part correctly,the purpose of it is that keep the namespace of closure type within default argument consistent whether the lambda-expression is at the point of function declaration or the point of call). If I misunderstand these rules,How to interpret them correctly?
UPDATE:
the version 9.1 or higher of gcc can not complie the code mentioned in #1664,it will report error (the complie result)
Questions:
1.Does the non-dependent default argument of function template or non-template function need to determined every time when the corresponding function called?
2.what does "the definition for the default argument" mean?Is this wording strictly?(In other word,my understanding is,what actullay the added rules want to express is that the namespace of closeure type is that of a function declartion where contain a default argument which contain the corresponding lambda-expression,Right?if my understanding about this is wrong,correct me)
Default arguments are evaluated every time they are called, but this is a runtime property: calls are counted not by source line but by actual control flow. Separately, a default argument for a templated function is considered to be a definition and is instantiated when needed, up to once per specialization of the function (with the usual proviso about multiple points of instantiation that must agree). CWG1664 was a very narrow issue based on how that instantiation was worded: by introducing a fictitious function template, it left open the possibility that the lambda’s declaration “physically” moved. The fix really does affect only ADL.
Your func example instead illustrates the usual name-lookup rules in templates: no matter how many times and whence test’s default argument is instantiated, func in it is not a dependent name and thus finds func(T,float) (every time). MSVC has famously never implemented this rule correctly (because, to be fair, their implementation predates the rule and they’ve only recently started the requisite (and nearly complete) rewrite of their template support).
Meanwhile, recent GCC is plainly buggy with the CWG1664 example: note that it complains about foo being used but not defined, contradicting both the plainly visible { } and its prior error message about not finding it.
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 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.