Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aren't template class member functions compiled at instantiation?

I found a strange issue when porting my code from Visual Studio to gcc. The following code compiles fine in Visual Studio, but results in an error in gcc.

namespace Baz
{
   template <class T>
   class Foo
   {
   public:
      void Bar()
      {
         Baz::Print();
      }
   }; 

   void Print() { std::cout << "Hello, world!" << std::endl; }
}

int main()
{
   Baz::Foo<int> foo;
   foo.Bar();

   return 0;
}

My understanding is that this should compile OK, as the class shouldn't be compiled until the template is instantiated (which is after Print() is defined). However, gcc reports the following:

t.cpp: In member function 'void Baz::Foo::Bar()': Line 8: error: 'Print' is not a member of 'Baz'

Who's right? And if gcc is right, why?

like image 938
Shirik Avatar asked Jul 15 '10 17:07

Shirik


2 Answers

gcc is right. Templates are compiled in two stages: once when they are parsed, and once when they are instantiated.

At parse time, anything that does NOT depend on the template parameters is checked, so in this case Baz::Print is looked up, and found not to have been declared, so this is an error.

If the call had been T().Print() or something else that depended on the type T then the lookup would be delayed until instantiation time (or least partially delayed --- see below.)

Qualified names like Baz::Print are always looked up at definition time unless the qualification itself depends on the template parameter (e.g. T::Print), though overload resolution is deferred to instantiation time. This means that you cannot add to the overload set for qualified names after the template has been declared.

Unqualified names such as just Print are looked up at instantiation time if any of the function arguments depend on a template parameter. So Print(T()) would be looked up at instantiation time, whilst Baz::Print(T()) is not. It's worth noting that the instantiation-time lookup is restricted to those names visible at the point of declaration and those found through ADL, so for Foo<int>, even a plain Print(T()) should not find Baz::Print if it was declared after the template (like in the example).

To make the example code work, either define Foo<T>::Bar after the definition of Print, or forward-declare Print before the definition of Foo.

like image 161
Anthony Williams Avatar answered Nov 15 '22 09:11

Anthony Williams


gcc is right. It is because Baz is a namespace and namespaces are parsed top to bottom, so the declaration of Baz::Print is not visible from inside Foo (since it is beneath it).

When the template is instantiated, only names visible from the template definition are considered, not counting Koenig lookup (which wouldn't change anything in your case).

If Baz were a struct or class, your code would work, since these are parsed in two phases (first declarations, then bodies), so anything declared in a struct or class is visible inside eg. member functions, regardles of their order in the source file.

You can make it work by declaring Baz::Print before Foo.

Quoting the standard:

14.6.3 Non-dependent names

Non-dependent names used in a template definition are found using the usual name lookup and bound at the point they are used.

14.6.4 Dependent name resolution

In resolving dependent names, names from the following sources are considered:

  • Declarations that are visible at the point of definition of the template.
  • Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context.

(end quotation)

When Print is nondependent (as it is now), it wouldn't be found since it is looked up before its declaration (in the template definition context). If it were dependent, it wouldn't be the first case (same as when nondependent), and Baz is not associated with int (the template parameter) in any way, so it wouldn't be searched according to the second case either.

like image 32
jpalecek Avatar answered Nov 15 '22 08:11

jpalecek