Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Member of base class template is out of scope in derived class template with same template argument

Tags:

c++

templates

The following code gives me a compilation error 'value' was not declared in this scope.

template<class T>
struct Base {
    int value;
};

template <class T>
struct Derived : public Base<T> {
    int getValue() { return value; }
};

I find it extremely odd that

  • if Derived inherits from Base<std::string>, the code compiles,
  • if I return Base<T>::value, the code compiles.

Why doesn't the code compile as it is? In what way is 'value' not declared in the scope of Derived<T>::getValue()?

like image 688
Oswald Avatar asked Apr 04 '13 14:04

Oswald


1 Answers

Because value is an unqualified name, and during the first phase of name lookup, the compiler will have no clue that this is a data member inherited from a base class (it hasn't instantiated Base<T> yet). Thus, it will search the global namespace, and find no variable called value; consequently, it will emit an error.

Here is a typical approach to solve this problem:

template <class T>
struct Derived : public Base<T> {
    int getValue() { return this->value; }
    //                      ^^^^^^
};

Explictly dereferencing this tells the compiler that the name that follows is the name of a (possibly inherited) data member, and the lookup should be delayed to the point where the member function is actually instantiated. Of course, your solution of doing:

return Base<T>::value;

Is equally good, because it also tells the compiler that value is inherited from the base class Base<T>.

For what concerns deriving from Base<std::string>, the compiler can immediately go and look up whether Base<std::string> contains a data member named value (because it does not depend on any template parameter) and if that is the case, it will be able to determine that the expression is well-formed.

However, if your base class is Base<T>, where T is unknown during the first phase of name lookup, the compiler can't tell what value is (specializations of Base for different Ts may even not have value at all).

Paragraph 14.6/3 of the C++11 Standard:

In the definition of a class or class template, if a base class 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. [...] [Example:

struct A {
    struct B { / ... / };
    int a;
    int Y;
};

int a;

  template<class T> struct Y : T {
    struct B { / ... / };
    B b; // The B defined in Y
    void f(int i) { a = i; } // ::a
    Y* p; // Y<T>
  };

Y<A> ya;

The members A::B, A::a, and A::Y of the template argument A do not affect the binding of names in Y<A>. —end example ]

like image 152
Andy Prowl Avatar answered Sep 20 '22 22:09

Andy Prowl