Consider:
template <typename T>
class Base
{
public:
static const bool ZEROFILL = true;
static const bool NO_ZEROFILL = false;
}
template <typename T>
class Derived : public Base<T>
{
public:
Derived( bool initZero = NO_ZEROFILL ); // NO_ZEROFILL is not visible
~Derived();
}
I am not able compile this with GCC g++ 3.4.4 (cygwin).
Prior to converting these to class templates, they were non-generic and the derived class was able to see the base class's static members. Is this loss of visibility in a requirement of the C++ spec or is there a syntax change that I need to employ?
I understand that each instantiation of Base<T>
will have it's own static member "ZEROFILL
" and "NO_ZEROFILL
", that Base<float>::ZEROFILL
and Base<double>::ZEROFILL
are different variables, but i don't really care; the constant is there for readability of the code. I wanted to use a static constant because that is more safe in terms of name conflicts rather than a macro or global.
Try this program
in the
T get(void){return this->x+2;}
line u can also use the scope resolution (::) operator. for example, try replace the line withThat's two-phase lookup for you.
Base<T>::NO_ZEROFILL
(all caps identifiers are boo, except for macros, BTW) is an identifier that depends onT
.Since, when the compiler first parses the template, there's no actual type substituted for
T
yet, the compiler doesn't "know" whatBase<T>
is. So it cannot know any identifiers you assume to be defined in it (there might be a specialization for someT
s that the compiler only sees later) and you cannot omit the base class qualification from identifiers defined in the base class.That's why you have to write
Base<T>::NO_ZEROFILL
(orthis->NO_ZEROFILL
). That tells the compiler thatNO_ZEROFILL
is something in the base class, which depends onT
, and that it can only verify it later, when the template is instantiated. It will therefore accept it without trying to verify the code.That code can only be verified later, when the template is instantiated by supplying an actual parameter for
T
.The problem you have encountered is due to name lookup rules for dependent base classes. 14.6/8 has:
(This is not really "2-phase lookup" - see below for an explanation of that.)
The point about 14.6/8 is that as far as the compiler is concerned
NO_ZEROFILL
in your example is an identifier and is not dependent on the template parameter. It is therefore looked up as per the normal rules in 3.4.1 and 3.4.2.This normal lookup doesn't search inside
Base<T>
and so NO_ZEROFILL is simply an undeclared identifier. 14.6.2/3 has:When you qualify
NO_ZEROFILL
withBase<T>::
in essence you are changing it from being a non dependent name to a dependent one and when you do that you delay its lookup until the template is instantiated.Side note: What is 2-phase lookup:
The above example is compiled as follows. The compiler parses the function body of
foo
and see that there is a call tobar
which has a dependent argument (ie. one that is dependent on the template parameter). At this point the compiler looks up bar as per 3.4.1 and this is the "phase 1 lookup". The lookup will find the functionvoid bar (int)
and that is stored with the dependent call until later.When the template is then instantiated (as a result of the call from
main
), the compiler then performs an additional lookup in the scope of the argument, this is the "phase 2 lookup". This case that results in findingvoid NS::bar(A const &)
.The compiler has two overloads for
bar
and it selects between them, in the above case callingvoid NS::bar(A const &)
.Seems to compile ok in vs 2008. Have you tried: