If a normal function calls a function that has not been declared yet, I get a compile-time error:
void foo(int x)
{
bar(x); // ERROR: bar has not been declared yet
}
void bar(int x)
{
std::cout << x << '\n';
}
int main()
{
foo(42);
}
The fix is to either forward-declare the called function, or to switch the order of definitions.
However, these fixes do not seem to be necessary with function templates:
template<typename T>
void foo(T x)
{
bar(x); // OKAY
}
template<typename T>
void bar(T x)
{
std::cout << x << '\n';
}
int main()
{
foo(42);
}
This compiles just fine. Why is that? When the compiler sees bar(x)
, why does it not complain?
(I am using g++ 4.6.3)
This is a "why is the sky made out of bricks" type question. Ie, a question that asks why something false is true. It is not the case that in C++ your code is legal.
Live example, as you can see in gcc 4.8 this does not actually compile.
I guess the question "why does gcc 4.6 let this code compile" remains. One of the things that compilers did early on when writing template
expanders was to treat them as something similar to macros. Very little would be done when they where declared, and everything would be looked up when they where instantiated.
Compilers now tend to do more thing when the template
is declared, and less when it is instantiated. This is what the C++ standard requires, or is at least closer.
As it happens, ADL can get around this: bar
lookups that find bar
via ADL do not have to be visible at the point where foo
is written, but rather at the point of instantiation.
The gcc 4.8 error message is pretty self explanatory:
prog.cpp: In instantiation of ‘void foo(T) [with T = int]’:
prog.cpp:16:7: required from here
prog.cpp:6:10: error: ‘bar’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
bar(x); // OKAY
^
prog.cpp:10:6: note: ‘template<class T> void bar(T)’ declared here, later in the translation unit
void bar(T x)
^
these requirements may have been changed or clarified in C++11, so it is possible that gcc 4.6's behavior was legal under the C++03 standard.
When the compiler first sees bar(x)
, it doesn't know x
's type, hence it can't look up the correct bar
. Only when you instantiate foo
, T
and therefore x
's type are known and bar(x)
can be looked up.
Note that this work only for dependent expression, i.e. expressions that depend on a template parameter. If you add bar(42)
, it will fail to compile even if it is later instantiated with T==int
.
You might also want to google "two-phase lookup" for further information. Only recent versions of GCC implement those rules correctly, as some checks also need to be done during the first phase of parsing the template. As pointer out by Yakk, newer versions of GCC reject your code, so always check with up-to-date versions of GCC or Clang to be on the safe side.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With