Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't a derived template class have access to a base template class' identifiers?

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.

like image 927
cheshirekow Avatar asked Aug 06 '09 16:08

cheshirekow


People also ask

Can a template base class derived?

It is possible to inherit from a template class. All the usual rules for inheritance and polymorphism apply. If we want the new, derived class to be generic it should also be a template class; and pass its template parameter along to the base class.

Does template allow us to define generic classes and functions?

A C++ template is a powerful feature added to C++. It allows you to define the generic classes and generic functions and thus provides support for generic programming. Generic programming is a technique where generic types are used as parameters in algorithms so that they can work for a variety of data types.

Can a non template class have a template member function?

A non-template class can have template member functions, if required. Notice the syntax. Unlike a member function for a template class, a template member function is just like a free template function but scoped to its containing class.

What is the difference between class template and template class?

A class template is a template that is used to generate classes whereas a template class is a class that is produced by a template.


2 Answers

That's two-phase lookup for you.

Base<T>::NO_ZEROFILL (all caps identifiers are boo, except for macros, BTW) is an identifier that depends on T.
Since, when the compiler first parses the template, there's no actual type substituted for T yet, the compiler doesn't "know" what Base<T> is. So it cannot know any identifiers you assume to be defined in it (there might be a specialization for some Ts 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 (or this->NO_ZEROFILL). That tells the compiler that NO_ZEROFILL is something in the base class, which depends on T, 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.

like image 138
sbi Avatar answered Oct 19 '22 04:10

sbi


The problem you have encountered is due to name lookup rules for dependent base classes. 14.6/8 has:

When looking for the declaration of a name used in a template definition, the usual lookup rules (3.4.1, 3.4.2) are used for nondependent names. The lookup of names dependent on the template parameters is postponed until the actual template argument is known (14.6.2).

(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:

In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope 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.

When you qualify NO_ZEROFILL with Base<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:

void bar (int);  template <typename T> void foo (T const & t) {   bar (t); }   namespace NS {   struct A {};   void bar (A const &); }   int main () {   NS::A a;   foo (a); } 

The above example is compiled as follows. The compiler parses the function body of foo and see that there is a call to bar 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 function void 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 finding void NS::bar(A const &).

The compiler has two overloads for bar and it selects between them, in the above case calling void NS::bar(A const &).

like image 32
Richard Corden Avatar answered Oct 19 '22 04:10

Richard Corden