1

I have classes Base, Derived1, Derived2, etc.
It is compilable (below).

class Base{  };
class Derived1 : public Base{
    public: Derived1* operator->() { return this; }
    public: void f1(){}
};
class Derived2 : public Base{
    public: Derived2* operator->() { return this; }
};
class Derived3 : public Derived2{
    public: Derived3* operator->() { return this; }
};
int main(){//test case (my objective is to make all these work)
    Derived1 d1;  d1->f1();
    Base b=d1;              //non harmful object slicing
    Derived3 d3;
    Derived2 d2=d3;
}

Edit: I believe it is a non-harmful object slicing, and I think it is unrelated to the question.

Then, I want the operator->() to be inside Base, so I don't have to implement in all DerivedX class.

This is my attempt so far, using CRTP. It is uncompilable at # :-

class Base{  };
template<class T1,class T2>class Helper{
    public: T2* operator->() { return static_cast<T2*>(this); }
};
class Derived1 : public Helper<Base,Derived1>{
    public: void f1(){}
};
class Derived2 : public Helper<Base,Derived2>{    };
class Derived3 : public Helper<Derived2,Derived3>{    };
int main(){
    Derived1 d1;  d1->f1();
    Base b=d1;                    //#
    Derived3 d3;     
    Derived2 d2=d3;
}

I have read these two promising links (below), and do little progress (above) :-

Wiki states that casting Derive to Base is quite impossible for CRTP, so I feel that there might be no solution using CRTP.

Question:

  • How to move the operator-> to some kind of a base class to avoid code duplication?
    (with or without CRTP are both OK)
  • Is there any solution using CRTP? In other words, is CRTP not suitable for this job?

I am new to CRTP (just play with it today). Sorry if it is duplicated.

Community
  • 1
  • 1
javaLover
  • 6,347
  • 2
  • 22
  • 67
  • 1
    First of all you need to read about [*object slicing*](https://en.wikipedia.org/wiki/Object_slicing). Then for your problem, it is simply that in the second example the derived classes are not derived from `Base`. Inheritance is a "is-a" relationship, but e.g. `Derived1` is *not* a `Base` but a `Helper`. – Some programmer dude Mar 07 '17 at 10:09
  • @Some programmer dude About "is-a": Yes, the wiki state about that. I understand. It is like a direct disadvantage of CRTP. Is there any workaround? ....... About object slicing: In this specific case, I am not scared by the slicing (at main). I accept that I got some compiler warning. :) – javaLover Mar 07 '17 at 10:12
  • The workaround is to modify the `Base` class. Not invent some `Helper` class. But it still won't work with your initialization of the `b` object, since `b` is ***not*** a `Derived1` object but a `Base` object, the casting will be wrong. If you attempt `b->f1()` you will have *undefined behavior* (if it even builds). – Some programmer dude Mar 07 '17 at 10:16
  • @Some programmer dude Thank! By the way, I want `d1->f1()` not `b->f1()`. I want only upcast. Sorry if my variable names are confusing. – javaLover Mar 07 '17 at 10:20
  • Make `Helper` derive from `T1` - then `Derived1` et al will actually derive from `Base` (indirectly). It's not clear to me what the purpose of `Base` class is, though, and why it's important that all `DerivedX` inherit from it. – Igor Tandetnik Mar 07 '17 at 14:58

1 Answers1

2

Put aside the slicing of your objects, your example works just fine if you define your helper as:

template<class T1, class T2>
class Helper: public T1 {
public:
    T2* operator->() {
        return static_cast<T2*>(this);
    }
};

That is:

  • Derive from T1 as if in a pure mixin based approach
  • Use T2 as if in a pure CRTP approach

If you consider the declaration of Derived1:

class Derived1: public Helper<Base, Derived1>;

It goes without saying that now Base b = d1; works, for Derived1 inherits directly from Base.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • It will take time to digest, but I strongly believe that this is a correct solution. Is there any specific technical word for this CRTP approach? ... It looks cool. – javaLover Mar 08 '17 at 02:24
  • @javaLover Why is that difficult to digest? You want an intermediate class template that inherits from its first template parameter and adds crtp stuff using the second template parameter. You simply forgot to do the first step to the end. What was the purpose of `T1` otherwise? It's unused in your example, right? – skypjack Mar 08 '17 at 06:31
  • Agree, I just felt dizzy. XD – javaLover Mar 08 '17 at 06:33
  • 1
    @javaLover Ahahah. It happens. Anyway, the idiom is called _mixin_ or _inheritance from above_. You are mixing up it and crtp, as mentioned in the answer. – skypjack Mar 08 '17 at 06:35
  • Wow thank. I just found its own [wiki page](https://en.wikipedia.org/wiki/Mixin). – javaLover Mar 08 '17 at 06:38