Specification rule of "definition of a static data member is in the scope of its class" with a templated base class
GCC and Clang are both correct.
Particularly, from [temp.dep]/3 [emphasis mine]:
In the definition of a class or class template, the scope of a dependent base class is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member. [ Example:
typedef double A; template<class T> class B { typedef int A; }; template<class T> struct X : B<T> { A a; // a has type double };
The type name
A
in the definition ofX<T>
binds to thetypedef
name defined in the global namespace scope, not to thetypedef
name defined in the baseclass B<T>
. — end example ] [...]
In the definitions the static data members x1
and y1
of the of derived class template foobar
:
template<int VALUE>
int foobar<VALUE>::x1 = foobar::foo();
template<int VALUE>
int foobar<VALUE>::y1 = foobar::z;
you are using the injected-class-name ([temp.local]/3) to access the dependent names ([temp.res]/9) foo
and z
from the base
class template, whereas the unqualified names used in the definitions of the static data members x2
and y2
will not examine the scope of the dependent base class.
Note that we may bring in the names of the dependent base
class into the scope of the derived class, redefining foobar
as
template<int VALUE>
struct foobar: base<VALUE> {
using base<VALUE>::foo;
using base<VALUE>::z;
static int x1;
static int x2;
static int y1;
static int y2;
};
in which case the output of your program will be
10 10 -1 -1
This is just another case of the binding rules for non-depended names, which are looked up and bound at the point of template definition. (Not to be confused with ADL! Even while ADL would help with the call to foo()
if it had any parameter.)
For the full details please read here: https://en.cppreference.com/w/cpp/language/dependent_name
Similar artifact happens with calling a function from template base class:
template <typename T>
struct A
{
void f(){}
};
template <typename T>
struct B : A<T>
{
void g()
{
A<T>::f(); // of course works
this->f(); // this-> makes it depended, so it's bound only on instantiation time
f(); // compilation error, no f() found
using A<T>::f; // make the name available (and depended on the template)
f(); // now it works
}
};
https://gcc.godbolt.org/z/39KGvM