1

I've this kind of code

template <typename D, typename T>
class tl1 {
    std::list<T> mTs ;
public:
    T & getTbyName() const ;
}

template <typename T, typename C>
class tl2 {
public:
    std::string getName() { return mName ; }
private:
    C & mC ;
    std::string mName
}

class c2 ;

class cl1 : tl1<cl1, cl2>  {

}
class cl2 : tl2<cl2, cl1>  {

}

How could I check (at compile time) that T is cl2 type or derived from cl2 and C is cl1 type or derived from cl1. I need to be sure about cl2 type or the getTbyName will be a mess.

Thank you for your time
Quentin

  • There is no "derived from cl1" or "derived from cl2". cl1 and cl2 aren't classes. They're class templates. They become classes only when a template argument is specified, and only at that point does "derived from" start to make sense. Please edit your question to accurately state what you're after. We're not mind readers, we can make guesses, but there's a very good chance in this case that those guesses will be wrong. –  Feb 26 '16 at 22:49
  • @hvd does it make much more sense now? – Quentin Huot-Marchand Feb 26 '16 at 22:55
  • 1
    Well, I can tell that the limited impression I got from your original question was definitely wrong. But I'm still not quite sure what it's supposed to be. If you want `tl1` and `tl2` to have a hard dependency on `cl1` and `cl2`, I'm not sure why templates are useful here at all. –  Feb 26 '16 at 23:00
  • Because there is plently of memory managment between `cl1` and `cl2` and it's something comon between `cl1bis` and `cl2bis` so `tl1` and `tl2` care about that. Also I want got get a method like `T& getTbyName() const` – Quentin Huot-Marchand Feb 26 '16 at 23:06

3 Answers3

8

You may use static_assert and std::is_base_of.

#include <type_traits>

struct B {};

template <typename T>
class C {
    static_assert(std::is_base_of<B, T>::value, "T should inherit from B");
};

struct D : public B {};
struct KO {};


template class C<B>;
template class C<D>;
template class C<KO>;

int main()
{
}

main.cpp: In instantiation of 'class C<KO>':
main.cpp:16:16:   required from here
main.cpp:7:5: error: static assertion failed: T should inherit from B
     static_assert(std::is_base_of<B, T>::value, "T should inherit from B");
     ^
======= clang =======
main.cpp:7:5: error: static_assert failed "T should inherit from B"
    static_assert(std::is_base_of<B, T>::value, "T should inherit from B");
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:16:16: note: in instantiation of template class 'C<KO>' requested here
template class C<KO>;
               ^

Online Demo

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Note that in most cases you really want `is_convertible` rather than `is_base_of`, because `is_base_of` will return true for private bases too. – Brian Bi Feb 26 '16 at 23:34
  • Thank you for your answer, I tried to apply your explanation to my code but I think there is unsolvable issue due to forward declaration right? [demo](http://coliru.stacked-crooked.com/a/418a204b6aa72705) – Quentin Huot-Marchand Feb 26 '16 at 23:51
1

Use std::is_base_of from <type_traits> in conjunction with std::enable_if:

template <typename T,
  typename = typename std::enable_if<std::is_base_of<cl2, T>::value>::type>
class cl1 {
    std::list<T> mTs ;
}
Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
  • 2
    Strictly speaking, this SFINAE's out rather than erroring, which is rather different. – Puppy Feb 26 '16 at 22:39
  • @Puppy: Yeah, though I’m not sure there’s a logical place to put a `static_assert`. The constructor of `cl1`? It’s nice to say directly “this class only exists for those types `T` such that `cl2` is a base of `T`”. – Jon Purdy Feb 26 '16 at 22:41
  • Thank you for your help but I still have one question what `typename = typename [...]` mean? I understand the [...] – Quentin Huot-Marchand Feb 26 '16 at 22:47
  • 2
    @QuentinHuot-Marchand, It's an unnamed type parameter with a default. The second `typename` is there because it's a dependent type name. – chris Feb 26 '16 at 22:47
  • 2
    @JonPurdy, I place them at the very beginning of the class if possible. They don't have to be in a function. – chris Feb 26 '16 at 22:49
  • @chris: Ah, TIL! Thanks. – Jon Purdy Feb 26 '16 at 22:52
1

When you want to figure out if A is a base of B, your compiler needs to know the type definitions or A and B must be the same (A is a base of A).

So you want to check if one template argument is derived from a forward declared type.

Judging from your later post, you also want to check that your CRTP base classes are actually used as base classes to one of the arguments.

In the latter case, the compiler does not have all the information when the CRTP base class is specialized, because the derived class has not been defined.

Thus, you have to defer the evaluation until a later stage, when the compiler has the necessary information. To do this, let's create a helper:

#include <utility>

class cl2;
class cl1;

template <typename Defer, typename Base, typename Derived>
struct deferred_is_base_of : std::is_base_of<Base, Derived>
{
};

In theory, this class could be specialized later on, so the compiler can not assume that defered_is_base_of is the same as std::is_base_of.

Assuming that we need a constructor (any other "required" function would also be OK):

template <typename Derived, typename Value>
class tl1
{
public:
  template <typename Defer = void>
  tl1()
  {
    static_assert(deferred_is_base_of<Defer, cl2, Value>::value,
                  "Value has to be derived from cl2");

    static_assert(deferred_is_base_of<Defer, tl1, Derived>::value,
                  "Derived has to be derived from tl1");
  }
};

The constructor is a template with a default parameter. Thus, you can call it like always. But the compiler has to wait for you to do it because you might use a different template parameter.

This does the trick. The compiler will evaluate the static assert only when the constructor is called. And by then, there will be enough information.

The second template works the same way:

template <typename Derived, typename Value>
class tl2
{
public:
  template <typename Defer = void>
  tl2()
  {
    static_assert(deferred_is_base_of<Defer, cl1, Value>::value,
                  "Value has to be derived from cl1");

    static_assert(deferred_is_base_of<Defer, tl2, Derived>::value,
                  "Derived has to be derived from tl2");
  }
};

class cl1 : tl1<cl1, cl2>
{
  cl1()
  {
  }
};

class cl2 : tl2<cl2, cl1>
{
  cl2()
  {
  }
};

int main() {}

Deferring template evaluation is a powerful tool in your TMP toolbox.

EDIT: As @Brandon pointed out, the deferred base of is not required here. It is enough to put the check into the constructor to defer here.

Community
  • 1
  • 1
Rumburak
  • 3,416
  • 16
  • 27