Why can I call function templates without forward declarations?

If a normal function calls a function that has not yet been declared, 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 ​​that either the forward-declare function is called, or to switch the order of definitions.

However, these fixes do not seem necessary using 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 this? When the compiler sees bar(x) , why doesn't it complain?

(I am using g ++ 4.6.3)

+4
source share
3 answers

This is a question of why not brick. That is, a question that asks why something false is true. This is not the case when in C ++ your code is legal.

A living example , as you can see in gcc 4.8, this does not actually compile.

I assume the question is "why gcc 4.6 allows you to compile this code." One of the things that compilers did at an early stage when writing template extenders was to treat them as something like macros. Very little would be done when they announced, and everything would look when they instantiated.

Compilers now tend to do more things when a template declared, and less when it is created. This is what the C ++ standard requires, or at least closer.

As it happens, ADL can get around this: bar requests that find bar through ADL should not be visible at the point where foo is written, but rather at the instantiation point.

The gcc 4.8 error message is pretty clear:

 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 can be changed or clarified in C ++ 11, so it is entirely possible that the behavior of gcc 4.6 was legal in accordance with the C ++ 03 standard.

+10
source

When the compiler first sees bar(x) , it does not know the type of x , so it cannot find the correct bar . Only when you instantiate foo , T and therefore the type x known and bar(x) can be found.

Note that this only works for a dependent expression, that is, an expression that depends on a template parameter. If you add bar(42) , it will not be able to compile even if it was later created using T==int .

You may also want a two-phase Google search for more information. Only the latest versions of GCC correctly follow these rules, as some checks also need to be performed during the first stage of template analysis. As a Yakk pointer, newer versions of GCC reject your code, so always check with current versions of GCC or Clang to be safe.

+9
source

A function template is not a function; this is a recipe for creating functions as soon as the template parameters are known.

The compiler cannot see what bar means when it sees the definition of the template foo , because what it means may depend on what T. is. Therefore, it just remembers that it uses the name bar , which will need to be processed later.

When you call foo(42) , the compiler must produce (create an instance) a real function, and at that moment it searches for names that it could not have before, finds your template template (and calls its creation) and all is well.

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

+4
source

Source: https://habr.com/ru/post/1501077/


All Articles