Question

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)

Was it helpful?

Solution

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.

OTHER TIPS

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.

A function template isn't a function; it's a recipe for making functions, once the template parameters are known.

The compiler can't look up what bar means when it sees the foo template definition, because what it means could depend on what T is. So it just remembers that there's a use of the name bar that will need to be worked out later.

When you call foo(42) the compile has to produce (instantiate) the real function, and at that point it looks up the names it wasn't able to before, finds your bar template (and triggers instantiation of that too) and all is well.

For a normal function all names can be looked up when the function is defined, and so they must be properly declared at that point.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top