templated classes and inheritance.

In the following code, I have a templated class, that derives from another templated class.

For the code to be valid, I found that every reference to a base class member from the derived class needed to be fully specified (with thingbase<T>::. That seems redundant. Moreover, members accessed with this-> are exceptions; they don't need any specification of the parent class, which appears inconsistent.

Naturally, members accessed from the main code also don't need the full specification. It is expected, but it only makes me wonder even more about the need for the full specification from within the class.

I assume there is a very good reason related to the mechanics of template resolution, but I fail to grasp it. Can someone explain?

I came to this because I am writing a family of classes with multiple template specializations and I realized that I kept repeating some common elements; in particularly complicated typedef derived from a template argument, I was hoping to find a way to avoid these repetitions. One way is to use template specialization of functions and variables from with one single class definition, but then one needs to keep repeating the same template selectors, and the code becomes even messier. Any suggestion?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <vector>

template<class T>
class thingbase{
  public:
  typedef typename std::vector<T> vec;
  T c;
  thingbase(T x):c(x){}
};


template<class T>
class thing:public thingbase<T>{
  public:
    thing(T x):thingbase<T>::thingbase(x){
      std::cout << this->c << std::endl;            // Why is this OK but  .. 
      std::cout << thingbase<T>::c << std::endl;    // specifying thingbase<T>:: is required here ? 
    }    
    typename thingbase<T>::vec makevec(){return typename thingbase<T>::vec({thingbase<T>::c});}  // and here !
};


int main ()
{
  auto t =thing<int>(7);

  auto vt = t.makevec();
  for(auto i:vt){std::cout << i << std::endl;} 
  std::cout << t.c << std::endl;
}
Last edited on
I wrote a nice explanation and then threw it away by mistake :(.

The name c in std::cout << c is a nondependent name.
The name c in std::cout << this->c is a dependent name: it depends on the template argument T (because this is implicitly type-dependent).

The distinction between "dependent" and "non-dependent" names affects name lookup. Non-dependent names are looked up and associated with a declaration immediately, whereas dependent names aren't until the template they depend on is instantiated.

Non-dependent name lookup doesn't check inside base class templates (dependent base classes), because the contents of that class can change depending on which specialization is chosen.

See:
https://en.cppreference.com/w/cpp/language/dependent_name
And the C++ FAQ
https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-members

One mitigation is to write some aliases and some using statements:
1
2
3
4
5
6
7
8
9
10
11
template<class T>
class thing:public thingbase<T>{
  public:
    using vec_type = typename thingbase<T>::vec;
    using thingbase<T>::c;
  
    thing(T x): thingbase<T>(x){
      std::cout << c << std::endl; 
    }    
    vec_type makevec() { return vec_type({c}); } 
};

Last edited on
Excellent, thank you!

I don't know if the explanation you scratched by mistake was better, but this one is very clear.

Last edited on
Topic archived. No new replies allowed.