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?
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
.
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:
Non-dependent names used in a template definition are found using the usual name lookup and bound at the point they are used.
In resolving dependent names, names from the following sources are considered:
(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.
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