How to declare a member in a base template where the type depends on the derived class?

Given a base class using CRTP, I am considering a member declaration in a base template where the type depends on 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 can simplify the implementation. I could add a second template to the template Baseand make BaseTraitsit easier or even get rid of it. However, the above snippet is already an attempt to remove the second template parameter. I am considering solutions that do not include a second template parameter for Base.

I tried something like the following, but it does not 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 am limited to C ++ 14.
  • Base must be a template.
  • .
+4
6

? appart -, auto ( ), .

, , , .

, : , , ++ N4140 [ .class]/2 ( ):

, , , ; [...]

+3

- :

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

class Derived : public Base<Derived, int> {};
+2

, .

#include <any>

template <class T> class Base {
    std::any id; // expensive, but cannot have T::TypeId here
 public:
    Base() : id(123) {}
    auto getId() { 
         return std::any_cast<typename T::TypeId>(id); 
    } // T::TypeId is OK inside a member function
};

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

?

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

struct BasicDerived {
    using TypeId = int;
};


using Derived = Base<BasicDerived>;
+1

, . .
, .

, Derived TypeID? , - .

TypeID: 1:1? 1:1 . TypeID? , TypeID Derived .
TypeID ? typedef?

? , typeid , ? DerivedBase typedef Derived, Base .

0

, ... :
, , .
typedef .
, typename , struct

template <class ThatClassWrapper>
class MyBase
{
protected:
    typedef typename ThatClassWrapper::TypeId TypeId;
    typedef typename ThatClassWrapper::RealClass ThatClass;
    TypeId typeIdValue;
    TypeId  GetTypeId() {   return typeIdValue; }
    std::vector<ThatClass*> storage;
};

class SomeClass;
namespace TypeIdBinding
{
    struct SomeClass
    {
        enum TypeId
        {
            hello, world
        };
        typedef ::SomeClass RealClass;
    };
}
class SomeClass: public MyBase<TypeIdBinding::SomeClass>
{
public:
    bool CheckValue(TypeId id)
    {   return id == typeIdValue;   }
};

Note that this class uses TypeId as defined in the template database, and named members are not displayed directly. You can fix this if the Base template is derived from a binding structure (confirmed that it compiles in this way). although I actually like it in C ++ 11, you can export or typedef just the enumeration name from another namespace and use this type name as a prefix for the enumeration members, helping to avoid name pollution.

0
source

Source: https://habr.com/ru/post/1690525/


All Articles