Lookup of dependent names in C++ template instantiation

1.9k views Asked by At

When I try to compile this code

// void foobar(int); 

template <class T>
struct Foo {
  void bar(T t) { foobar(t); };
};

void foobar(int);

template class Foo<int>;

with g++ 4.8.2 I get the following error message

foo.cc: In instantiation of ‘void Foo<T>::bar(T) [with T = int]’:
foo.cc:10:16:   required from here
foo.cc:5:27: error: ‘foobar’ was not declared in this scope, and no 
             declarations were found by argument-dependent lookup at 
               the point of instantiation [-fpermissive]
   void bar(T t) { foobar(t); };
                           ^
foo.cc:8:6: note: ‘void foobar(int)’ declared here, later in the translation unit
 void foobar(int);
      ^

(With clang 3.4 it's nearly the same).

First, I think the code is correct and should compile, since foobar is a dependent name in the template declaration and should be looked up only in phase two when the template is instantiated. When this is done in the last line, 'foobar(int)' is already declared. The code compiles, BTW, when I uncomment the topmost line, but both declarations are before the instantiation and so this should not matter.

Second, the error message itself seems contradictory to me. It says "no declarations were found at the point of instatiation" which is foo.cc:10:16, and it says it's declared "later" at foo.cc:8:6. For all that I know about numbers and the English language I would call that "before" not "later".

So, is it a bug in gcc or did I get something wrong? Since this seems to me a common usage pattern I can't quite believe that, however.

BTW: when I try out the second example of "Name Resolution for Dependent Types" at MSDN (http://msdn.microsoft.com/en-us/library/dx2zs2ee.aspx) with g++ the result is different from vc++, which (not generally, but in this specific case) would undermine this being a bug in g++.

3

There are 3 answers

7
M.M On BEST ANSWER

tl;dr Foo<int> doesn't invoke any ADL, but Foo<X> would (where X is a class type).


First of all, in this code foobar is a dependent name because of (C++14/N3936) [temp.dep]/1

In an expression of the form:

postfix-expression ( expression-list opt )

where the postfix-expression is an unqualified-id, the unqualified-id denotes a dependent name if [...]

  • any of the expressions in the expression-list is a type-dependent expression (14.6.2.2), or

and t is a dependent name because it is part of a declaration T t where T is a template parameter and thus a dependent type.

Moving onto dependent name resolution, there is [temp.dep.res]/1 which introduces the fact that names can be both looked up in the definition context, and the instantiation context, and defines where the instantiation context is. I have omitted that for brevity, but in this example template class Foo<int>; is the point of instantiation.

The next bit is [temp.dep.candidate]/1:

For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2) except that:

  • For the part of the lookup using unqualified name lookup (3.4.1), only function declarations from the template definition context are found.
  • For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.

Those last two parts are the "two phases" of two-phase lookup. (Note - this section changed in wording from C++11 to C++14, but the effect in the same).

In the first phase, 3.4.1, no names are found for foobar.


So we move onto the second phase. The actual places that names are looked up as described in 3.4.2. The text is long but the here are two of the relevant rules:

  • If T is a fundamental type, its associated sets of namespaces and classes are both empty.

  • If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes. [...]

So when you instantiate Foo<int>, then the second phase of lookup does not introduce any additional namespaces to search.

However, if you change your example to have struct X {}; and then change int to X everywhere, then the code does compile. This is because of the latter bullet point: ADL for an argument of class type does search the enclosing namespace of that class (which is the global namespace now), however ADL for an argument of built-in type does not search the global namespace.

6
Puppy On

Phase two lookup only includes name lookup rules that can't be applied in phase one- ADL, for example. Two-phase lookup is exactly that- some names are looked up in phase one, and some are looked up in phase two. This particular kind of name is a phase one name, as the compiler is perfectly capable of looking for foobar in the namespace(s) of the function during phase one.

Visual C++ does not implement two-phase name lookup.

1
MSalters On

Looks correct to me. While overload resolution is done only in phase 2, in phase 1 you already have to know that foobar(t) is a function call expression. If foobar names a type, t wouldn't even be a dependent name.