0

I'm stuck with templates problems since few days and you solve each of my problem at a time so thank you in advance.

So I've a template (tl1) who care about a uml composition, and another template (tl2) wich is the uml composed
So my goal is to not compile if the composed object is not a derived of tl2 and if typename D is not a tl1 derived.

Following this post and the help of this one I've got the following code:

#include <type_traits>
#include <list>
#include <string>

template <typename T, typename C>
class tl2 ;

template <typename D, typename T>
class tl1 {
private:
    static_assert(std::is_base_of<tl2<T, D>, T>::value, "T should     inherit from tl2");
    std::list<T> mTs ;
    tl1() {} ;
    friend D ;
public:
    T & getTbyName() const ;
};

template <typename T, typename C>
class tl2 {
    //static_assert(std::is_base_of<tl1<C, T>, C>::value, "D should inherit from Database");
public:
    std::string getName() { return mName ; }
private:
    C & mC ;
    std::string mName ;
};

class cl1 ;

class cl2 : public tl2<cl2, int>  {

};
class cl1 : public tl1<int, cl2>  {

};

My problem is this compile very well and I would like not. I would like not compile because D from tl1<D, T> must derived from tl1.
And actually class cl1 : public tl1<int, cl2> is not correct but it compile. So why?
It doesn't compile if I change cl1 to:

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

I understand why it doesn't compile after the change, but I do not understand why it compile before.

The fact is tl1 and tl2 will be in library, so I want to perform all checks in the library. I will not have control over derived so I'd like to be sure that implementation is tlX derived.

Thank you for your time again.
Quentin

Community
  • 1
  • 1
  • "My problem is this compile very well and I would like not." Why? Usually the issue is that something doesn't compile, and you'll want it to complile. "It doesn't compile if I change cl1 to:..." cl1 is already defined as you indicated, and this compiles without any issues. Your questions are unclear, and confusing. You need to clarify them. – Sam Varshavchik Feb 28 '16 at 14:20
  • @SamVarshavchik is the description more clear now? – Quentin Huot-Marchand Feb 28 '16 at 14:26
  • 1
    `std::is_base_of, T>::value` checks if T is derived from tl2 but where are you checking if `std::is_base_of::value` (D derived from Tl1). – Brandon Feb 28 '16 at 14:28
  • @Brandon I tried it but it doesn't compile I think it's because `D` is not known by `tl1` in `class cl1 : public tl1` – Quentin Huot-Marchand Feb 28 '16 at 14:51

2 Answers2

1

The problem with doing what you are trying to do is cyclical dependencies. std::is_base_of requires a complete type in order to work as far as I am aware.

Your code has TWO restrictions in tl1..

  • T must inherit from tl2
  • D must inherit from tl1

In the end, it ends up looking like:

tl1<T, D> where D inherits tl1<T, D> where D inherits tl1<T, D>

In other words, D will never be defined because Tl1 requires the definition of D as a template parameter but D must inherit from Tl1 which requires it.

Now if you remove the restriction on D, then the following code will compile as it should because the first restriction is met. However, if you uncomment the static_assert in tl1, it will NEVER compile because the definition of D depends on the definition of tl1 which depends on the definition of D and so on.. and so forth..

You'd get an error like:

invalid use of incomplete type 'class cl1'
     struct is_base_of
            ^
note: forward declaration of 'class cl1'

Code:

#include <type_traits>
#include <list>
#include <string>

template <typename T, typename C>
class tl2 ;

template <typename D, typename T>
class tl1 {
private:
    static_assert(std::is_base_of<tl2<T, D>, T>::value, "T should     inherit from tl2");
    //static_assert(std::is_base_of<tl1, D>::value, "D should inherit from tl1");
    std::list<T> mTs ;
    friend D ;
public:
    tl1() {}
    T & getTbyName() const ;
};

template <typename T, typename C>
class tl2 {
    //static_assert(std::is_base_of<tl1<C, T>, C>::value, "D should inherit from Database");
public:
    std::string getName() { return mName ; }
private:
    //C & mC ;
    std::string mName ;
};


class cl1;

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

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

int main() {
    cl1 a;
    cl2 b;
    return 0;
}

If you replace std::is_base_of with:

template<class B, class D>
struct is_base_of
{
  template<typename T> struct dummy {};
  struct Child : D, dummy<int> {};

  static B* Check (B*);
  template<class T> static char Check (dummy<T>*);

  static const bool value = (sizeof(Check((Child*)0)) == sizeof(B*));
};

It will give you the error:

recursively required by substitution of 'template<class T> static char is_base_of<B, D>::Check(is_base_of<B, D>::dummy<T>*) [with T = <missing>]'

Which in my opinion is much clearer as to what is happening.

TLDR: You can't.

Brandon
  • 22,723
  • 11
  • 93
  • 186
  • Thank you for your very constructive answer (and your time). It is why `tl1` constructor is private. But I don't understand why `class cl1 : public tl1` doesn't scream ... – Quentin Huot-Marchand Feb 28 '16 at 16:39
  • @QuentinHuot-Marchand, generally it is not permitted to use incomplete types with standard library templates, and you may simply get undefined behavior if you do. There are a few exceptions, but, you can't expect the compiler to scream when you use any template with an incomplete type -- there is no way to program the template so that it generates an error if used with an incomplete type, or detects this at all. – Chris Beck Feb 28 '16 at 17:50
  • @ChrisBeck I understand, I guess I have to deal with that. Thank you for your time – Quentin Huot-Marchand Feb 28 '16 at 17:52
  • @Brandon You can, you just have to defer the check until the compiler knows enough about the types. See my answer [here] (http://stackoverflow.com/a/35688195/2173029). – Rumburak Feb 28 '16 at 21:01
  • @Rumburak; Sure your code works because you change the requirements. The requirements does NOT state that "Value needs to inherit from cl1". It states that it needs to inherit from tl2 which is templated and that the second template parameter inherits from tl1. You're doing an inheritance from a subclass of tl1 and tl2 and neither of the subclasses are templated. – Brandon Feb 28 '16 at 22:27
  • @Rumburak; http://ideone.com/zG4y6l it compiles even without "deferred_base_of" due to the change in requirements you made. Unfortunately, this is not what is in the question.. – Brandon Feb 28 '16 at 22:33
  • 1
    @Brandon Thanks for pointing out that `deferred_base_of` is not needed (I admit, I am quite surprised). I did answer the question of this post (it checks for the wrong thing) and the former post, though. The point is that deferring the check can break the cycle. See and play here: http://ideone.com/H4hm3O – Rumburak Feb 29 '16 at 06:19
1

Your code compiles, because there is nothing wrong in the static assert.

cl1 specializes tl1 with

T = cl2 which inherits from tl2<cl2, int> D = int

And then you verify that

std::is_base_of<tl2<T, D>, T>::value == true

So let's replace T and D here:

std::is_base_of<tl2<cl2, int>, cl2>::value == true

And that is correct.

In other words, your static assert is probably not checking what it should. As of now, I am a bit confused what you want to achieve. I'll post an answer to your former question, but you should seriously think about making your names clearer. It will help to comprehend what you intend to achieve.

After feedback from @Brandon, I guess you might be looking for this: http://ideone.com/H4hm3O

Rumburak
  • 3,416
  • 16
  • 27