I have the following C++ code which outputs 12
:
#include <iostream>
using namespace std;
template<typename T>
T foo(T val) {
return val;
}
template<typename T>
T bar(T n) {
return foo(n);
}
int foo(int val) {
return 1;
}
int main() {
cout<<foo(2)<<bar(2)<<endl;
return 0;
}
If I change the definition of bar
to
int bar(int n) {
return foo(n);
}
without changing its position, the output is still 12
.
However, if I move the definitions of bar
to just before main
, the output changes to 11
. (compiler: g++ 14.1.1
)
My question is:
bar(2)
call the template version of foo
instead of the non-template overload even if both of them seem to be viable when bar
is instantiated?11
without changing the order of function definitions while maintaining the generality of the template foo definition?A follow-up question:
If foo
is independent and looked up at the first stage, why should the following code compile?
#include <iostream>
using namespace std;
struct C {};
template<typename T>
T bar(T n) {
return foo(n);
}
C foo(C val) {
std::cout << "foo\n";
return {};
}
int main() {
bar(C());
return 0;
}
Why does
bar(2)
call the template version of foo
In the original case, at the point where foo(n)
is called, only the function template version of foo
is viable. Even though there is two-phase lookup, there is no associated namespace for int
as int
is a built in type. That is, the non-templated version is not added to the candidate list here.
If you were to have a class type parameter instead of int
, the call to foo(n)
in the original case would've called the non-templated foo
(demo), because this time through, argument dependent lookup in phase two would have seen the non-template foo
is also a viable candidate.
In the second case, when you move the definition of bar
to just before main
, both versions (templated and non-template) are visible for foo(n)
. And since a non-template version is preferred over the templated version, we see 11
.
How can I modify the code to output
11
without changing the order of function definitions while maintaining the generality of the template foo definition?
You can add a forward-declaration for the non-templated foo
before bar
, as shown below:
#include <iostream>
using namespace std;
template<typename T>
T foo(T val) {
return val;
}
int foo(int val); //added this declaration
template<typename T>
T bar(T n) {
return foo(n);
}
int foo(int val) {
return 1;
}
int main() {
cout<<foo(2)<<bar(2)<<endl; //now this prints 11
return 0;
}