44

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.

cheshirekow
  • 4,797
  • 6
  • 43
  • 47
  • Because of the two-phase name lookup (which not all compilers use by default). There are 4 solutions to this problem: **1)** Use the prefix `Base::NO_ZEROFILL`, **2)** Use the prefix `this->NO_ZEROFILL`, **3)** Add a statement `using Base::NO_ZEROFILL`, **4)** Use a global compiler switch that enables the permissive mode. The pros & cons of these solutions are described in https://stackoverflow.com/questions/50321788/a-better-way-to-avoid-public-member-invisibility-and-source-code-bloat-repetitio – George Robinson May 14 '18 at 13:33

4 Answers4

48

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.

sbi
  • 219,715
  • 46
  • 258
  • 445
  • 1
    aw, you beat me to it by 20 seconds. +1 – jalf Aug 06 '09 at 16:15
  • 1
    @jalf: So for once, I'm the first. `:^>` Feel free to improve on it. – sbi Aug 06 '09 at 16:17
  • 2
    Interesting. Do inherited non-static members then require the same qualification? i.e. Base::memberFunction() – cheshirekow Aug 06 '09 at 16:25
  • 2
    @ceshirekow: Yes, basically everything behind a `::`, where there's something depending on a template parameter before the `::`, is "dependent", and this is one of the issues arising from this. (See for an explanation of why the `typename` is necessary sometimes, if you want to learn more about this. It has the same reason and you'll find good explanations everywhere.) – sbi Aug 06 '09 at 16:32
  • 1
    @sbi: Are you sure this is called 2-phase lookup? I always thought that term was just for argument dependent lookup. Do you have a reference in the standard for that? – Richard Corden Aug 06 '09 at 17:12
  • @Richard: Um, I think I explained that `NO_ZEROFILL` _is_ indeed argument-dependent. Depending on `T`, any specialization of `Base`could define `NO_ZEROFILL` to be anything -- or not define it at all. – sbi Aug 06 '09 at 18:20
  • No, "Argument dependent lookup" is the official name for Koenig lookup. When resolving foo(a,b), the name foo is looked up in the namespaces of typeof(a) and typeof(b). Nothing to do with templates. – MSalters Aug 07 '09 at 10:23
  • @MSalters: Sorry for the confusion. What I meant is that `NO_ZEROFILL` depends on a template argument. In that, it is argument-dependent. However, argument-dependent lookup is of course an already occupied term, so I shouldn't have used it. – sbi Aug 07 '09 at 12:01
  • @MSalters what @Richard was referring to was two phase lookup when unqualified dependent function names are looked up at instantiation. It's called "two phase lookup" because a part of the function name lookup is done at the definition context and another part (the part about ADL) is done when instantiating. But the term "two phase lookup" is not a standards-defined term, so people use that term differently. – Johannes Schaub - litb Dec 14 '10 at 13:39
  • There is a similar question, and I think the answer there is good. http://stackoverflow.com/questions/1120833/derived-template-class-access-to-base-class-member-data – kevin Jul 05 '16 at 20:23
  • @kevin That answer mostly tells how to work around the problem, only linking to some explanation. I consider that inferior, as external links might "rot". My answer attempts to explain the underlying problem. I have added the `this->` syntax to my answer (even though I'm not sure it's applicable for a member function default argument). Would that help? – sbi Jul 06 '16 at 06:35
  • @sbi Absolutely. Thanks. – kevin Jul 06 '16 at 14:01
30

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 &).

Richard Corden
  • 21,389
  • 8
  • 58
  • 85
  • 4
    +1, nice explanation :) Too bad you seem to have stopped answering on stackoverflow. I loved your elaborative and intensive discussions. – Johannes Schaub - litb Dec 14 '10 at 13:35
  • It's nice to be missed! I hope to get back to answering questions in the near future. – Richard Corden Dec 14 '10 at 13:59
  • @RichardCorden I'm not fully understanding your side note to illustrate the "2-phase lookup". As my understanding, the function name `bar` in the function body of `foo` is a dependent name, because its parameter is type-dependent. Shouldn't the name lookup of `bar` in `foo` also be postponed until the actual template argument is known? Actually, clang++ 3.8.1 doesn't yield any error about the name lookup even if I comment out the line `void bar (int);`. So I doubt the "phase 1 lookup" for `bar` is actually performed. – Carousel Aug 29 '16 at 13:09
  • @Carousel As the name is dependent an error is not expected during phase 1 lookup. Try the following: Add a new template overload above `foo(T const &)`, `template void bar (T);`. Change the overload in `namespace NS` have the same signature: `template void bar (T);`. Phase 1 lookup finds 2 overloads for `bar`, and phase 2 finds `NS::bar`. The compiler is unable to select a best match between the `bar(T)` functions. Now move the global `bar(T)` below `foo`. The code will compile successfully because phase 1 only finds `bar(int)`. – Richard Corden Aug 30 '16 at 10:31
1

Seems to compile ok in vs 2008. Have you tried:

public:
    Derived( bool initZero = Base<T>::NO_ZEROFILL );
Alan
  • 13,510
  • 9
  • 44
  • 50
  • 5
    Actually, VC doesn't do two-phase lookup. That's why it compiles there. And that's why it is a bad idea to create a template lib using VC -- you'll have a lot of stuff to fix when you need it on any other compiler. – sbi Aug 06 '09 at 16:16
  • the fixes are generally fairly trivial though. It's mostly a matter of inserting a lot of `typename`'s, and fixing the occasional 2-phase lookup. – jalf Aug 06 '09 at 17:13
  • 3
    @jalf: That's true except where it isn't. Among others, I've run into very nasty inter-dependency problems that weren't discovered with VC because VC would only really parse the template when it was instantiated -- and by then all dependent entities were in scope. During the first parse in a proper two-phase lookup, this fell to pieces and it took quite a while and a liberal sprinkling of the programmer's universal cure (indirection) to untangle the mess. The code was a lot harder to understand after that, and it would probably have been designed differently had the problem been known earlier. – sbi Aug 08 '09 at 11:46
0

Try this program

#include<iostream>
using namespace std;
template <class T> class base{
public:
T x;
base(T a){x=a;}
virtual T get(void){return x;}
};
template <class T>
class derived:public base<T>{
public:
derived(T a):base<T>(a){}
T get(void){return this->x+2;}
};
int main(void){
base<int> ob1(10);
cout<<ob1.get()<<endl;
derived<float> ob(10);
cout<<ob.get();
return 0;
}

in the T get(void){return this->x+2;} line u can also use the scope resolution (::) operator. for example, try replace the line with

T get(void){return base<T>::x+2;}
  • 1
    Please properly format your code. Explanation on why code works and where the problem in the original code is, is usually a good thing too. – stefan Apr 14 '15 at 07:26