darkdragon
darkdragon

Reputation: 460

C++ inherit template dependable types

I have a template and many derived classes. In all of these classes I want to use the template type and some dependent types of it (actually I have many types and many dependent types).

The following approach is quite bad, since I need to manually inherit (with using keyword) all the types from the parent class (see code example):

EDIT: I didn't know that it is important that the class where I want to use the types is also a template. I updated the code example accordingly.

template<class T>
class A {
  using TBase = T;
  using TDerived = T[10];
}

template<class T>
class B : A<T> {
  // NOTE i want the types TBase and TDerived here and in further children
  using TBase    = A<T>::TBase;
  using TDerived = A<T>::TDerived
}

class C : B<int> {
  // NOTE i want the types TBase and TDerived here and in further children
  using TBase    = B<int>::TBase;
  using TDerived = B<int>::TDerived
}

I also thought about templated namespaces so that I could define the types outside of the class - but they don't exist.

Does anyone have a better idea?

SOLUTION1: fix current approach
(based on the comments from Jan Hudec and the answer from Oguk)

  1. public inheritance
    I forget the keyword public: for classes (struct is public by default).
  2. missing typename
    The compiler warning for my (wrong) types where used in the class where misleading (although the ones for the using keyword directly hint to the missing typename keyword).
    Further notes about when to use typename can be found here: https://stackoverflow.com/a/7923419/3779655
  3. type interitance
    When I correctly specify the type (e.g. see 2. missing typename) then I can also use the current class name.

Resulting code:

template<class T>
class A {
public:
  using TBase = T;
  using TDerived = T[10];
};

template<class T>
class B : public A<T> {
public:
  // possiblity 1:
  using TBase = typename B::TBase;
  TBase memberB1;
  // possibility 2
  typename B::TBase memberB2;
};

class C : public B<int> {
public:
  // NOTE C is no template -> we don't have to do anything
  TBase memberC;
};

SOLUTION2: trait classes
(based on the answer from Oguk)

  1. use templated trait class instead of namespace
    This gives pretty much the same functionality.
  2. different availability of type information
    One thing I didn't think of before is to check which type information is available where and if further characters are needed to retrieve the needed type information from a parent class (see NOTE comments).

See code below:

template<class T>
struct TypesContainer {
  using TBase = T;
  using TDerived = T[10];
};

template<class T>
class A {
public:
  // TODO possible to directly inherit all types?
  //using TypesContainer<T>;

  // NOTE this is only possible for methods, not types
  using Types = TypesContainer<T>;
  typename Types::TBase memberA;
};

class B : public A<int> {
public:
  // NOTE here I have all template information via definition (by hand)
  using Types = TypesContainer<int>;
  typename Types::TBase memberB;
};

class C : public B {
public:
  // NOTE here I don't have the type information any more
  using Types = TypesContainer<A::Types::TBase>;
  typename Types::TBase memberC;
};

PROBLEM:
Is there a way to directly inherit all members of a namespace/class within the class scope (see TODO comment). Using the using keyword without assignement operator = does only work for methods not namespaces, classes and types.

Upvotes: 1

Views: 1117

Answers (1)

Oguk
Oguk

Reputation: 1147

First, if you are otherwise fine with your design, it should work if you inherit the base class publicly. This would work in the code you posted. However, if your example is very simplified, there might be another reason you cannot access the typedefs in your real code: The thing which could cause problems for you is if your derived class is actually also a templated class with the type parameter T, e.g. B<T>. This would make the base class A<T> dependent and its members would not considered during lookup if you use a non-dependent name (i.e. only TBase) to refer to them inside B<T>, so they would not be found. Referring to them as typename A<T>::TBase would solve that problem by making the name dependent names.

If you want to follow your other idea and have that type information available outside of the class (because it is conceptually more connected to T and not so much to the class A), instead of the "templated namespaces" you were thinking of, usually, trait classes are used for this purpose:

template <class T>
struct MyTraits {
    using TBase = T;
    using TDerived = T[10];
}

Then you can access those using declarations (or, alternatively, typedefs) from anywhere by using MyTraits<T>::TBase. This is effectively what you would achieve with a templated namespace. Don't forget to add the typename keyword if you use the using/typedef members of MyTraits in a dependent context, i.e. typename MyTraits<T>::TBase.

Upvotes: 2

Related Questions