Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to declare a member in a base template class where the type is dependent of the derived class?

Given a base class using CRTP, I'm looking at declaring a member in the base template class where the type is dependent of the derived class.

While the following works as intended:

template <class T> class BaseTraits;
template <class T> class Base {
    using TypeId = typename BaseTraits<T>::TypeId;
    TypeId id;
 public:
    Base() { id = 123; }
    TypeId getId() { return id; }
};

class Derived;
template <> class BaseTraits<Derived> {
public:
    using TypeId = int;
};

class Derived : public Base<Derived> {};

int main(int argc, char ** argv) {
     Derived foo;
     return foo.getId();
}

I wonder if I could simplify the implementation. I could add a second template parameter to the Base template, and make BaseTraits simpler or even get rid of it. However the above snippet is already an attempt to remove the second template parameter. I'm looking at solutions that doesn't involve a second template parameter for Base.

I've tried something like the following but it doesn't compile:

error: invalid use of incomplete type 'class Derived'

template <class T> class Base {
    using TypeId = typename T::TypeId;
    TypeId id;
 public:
    Base() { id = 123; }
    TypeId getId() { return id; }
};

class Derived : public Base<Derived> {
public:
    using TypeId = int;
};

int main(int argc, char ** argv) {
     Derived foo;
     return foo.getId();
}

UPDATE:

  • I'm limited to c++14.
  • Base must be a template.
  • Performance is a must.
like image 546
Flyer Avatar asked Dec 10 '17 20:12

Flyer


2 Answers

Is it possible to make a member type directly dependent on the derived class? Taking appart the result type of a member function declared with auto (deduced return type), it is not possible.

So the use of a type-trait as you do in your solution is the best and only solution.

The reason is that a base class must be a complete type when the derived class is defined: the compiler must first instantiate and parse the base class definition before it parses the derived class definition, C++ standard N4140 [derived.class]/2 (bold is mine):

The type denoted by a base-type-specifier shall be a class type that is not an incompletely defined class;[...]

like image 57
Oliv Avatar answered Oct 20 '22 01:10

Oliv


What about something like this:

template <typename T, typename TypeId> class Base 
{
private:
    TypeId id;
public:
    Base() { id = 123; }
    TypeId getId() {return id;}
};

class Derived : public Base<Derived, int> {};
like image 34
Killzone Kid Avatar answered Oct 19 '22 23:10

Killzone Kid