1

Why the *this of the derived<T> class is still of base<T> type?

I thought the typename X would take care of that.

If this looks ugly, what is a better way?

Failed Attempt:

template <typename T>
class Derived;

template <typename T, typename X>
class Base{
    public:
        T val;
        X f(void){
            return *this;
        }
};

template <typename T>
class Derived: public Base<T, Derived<T> >{
};


int main(void){
    Derived<int> B;
    Derived<int> C = B.f();
}

Error:

test4.cpp(9): error: no suitable user-defined conversion from "Base<int, Derived<int>>" to "Derived<int>" exists
              return *this;
                     ^
          detected during instantiation of "X Base<T, X>::f() [with T=int, X=Derived<int>]" at line 20

compilation aborted for test4.cpp (code 2)
rxu
  • 1,369
  • 1
  • 11
  • 29
  • 1
    `f` is a member function of `Base`, so `*this` has type `Base`. You'll need to use `return static_cast(*this);` – M.M Sep 20 '16 at 20:37
  • 1
    _"Why the *this of the derived class is still of base type?"_ How should it magically become the derived this without a `static_cast<>`?? – πάντα ῥεῖ Sep 20 '16 at 20:38
  • Your assertion isn't true. The `this` in question clearly occurs within `Base`. – Kerrek SB Sep 20 '16 at 20:41
  • This was magical. I didn't know what happened. Why `static_cast` is needed here?? – rxu Sep 20 '16 at 20:42
  • @rxu Because by itself, `*this` will always be the type of the class containing the function definition. For example, `Base::f()` will always see `this` as a `Base*`, and thus `*this` as a `Base`, even if you call it on an instance of `Derived`. Casting explicitly tells the compiler to treat it as the desired type, though, which solves the issue. – Justin Time - Reinstate Monica Sep 20 '16 at 20:46
  • I get this now. I want each of many derived classes to have a function that returns an object of the derived class type. Is defining such a function in the base class a design flaw? How to do that correctly? – rxu Sep 20 '16 at 20:49

4 Answers4

3

You may do the downcast with:

X f(){ return static_cast<X&>(*this);} 
Jarod42
  • 203,559
  • 14
  • 181
  • 302
2

You have

X f(void){
    return *this;
}

In this function, type of *this is still the base class type. There is no automatic conversion from the base type to X.

I am unable to suggest a clean solution since X can be anything at that point, not necessarily Derived<T>.

I can use:

template <typename T>
class Derived2 : public Base<T, double>
{
};

How's the base class supposed to deal with that?

The approach used in f() seems to be a design flaw.

Update

If Derived is guaranteed to have the form

template <typename T>
class Derived : public Base<T, Derived<T>>
{
};

Then, you can use:

X& f(void){
    return static_cast<X&>(*this);
}

Please note that I changed the return type from X to X&. It avoids the cost of making a copy every time you call the function.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • How to make multiple derived classes all have a function that returns itself? Is defining such a function in the base class not possible? How to fix that design flaw? – rxu Sep 20 '16 at 20:45
  • I guess copy-pasting the function to all the derived class is safe... Is there a better way? – rxu Sep 20 '16 at 20:47
  • @rxu, the solution might lie in the answer to the question "what do you want to do with the object after that?" – R Sahu Sep 20 '16 at 20:59
  • After that, I want to use it. It is an iterator over n-dimensional array. The derived classes are iterator over contiguous memory, iterator over elements that are x elements from each other, and another iterator. – rxu Sep 20 '16 at 21:08
  • I asked a new question here for iterator inheritance: http://stackoverflow.com/questions/39604076/iterator-inheritance-and-inheriting-this – rxu Sep 20 '16 at 21:39
2

When the compiler looks at class Base<int, Derived<int>> it has no reason to believe that there is actually a Derived<int> that inherited from it. One could do class Other : public Base<T, Derived<T>>{} and the conversion from class Base<int, Derived<int>> to Derived<int> would be incorrect, so an automatic conversion is not allowed.

If you make a rule that says that the second template parameter must be the derived type that inherits that base (which class Other violates) you can bypass the type system with a cast, but make sure the rule is not violated.

nwp
  • 9,623
  • 5
  • 38
  • 68
1

First of all, I would modify Base so that it accepts only one template parameter.
This way, it matches exactly the common form shown for the CRTP idiom.
Actually, it seems to me that there is no reason to use both T and Derived<T> as a template parameter in this case, for the latter already contains the former and T, Derived<T> is the pattern to which you want to adhere:

template<typename>
class Base;

template <typename T, template<typename> typename X>
class Base<X<T>> {
    // ...
};

template <typename T>
class Derived: public Base<Derived<T>>{
    // ....
};

Then you can use a promotion from the bottom as mentioned by @Jarod42:

X<T> f(void) {
    return *static_cast<X<T>*>(this);
}

Note that here you are making systematically a copy of the object and probably it is not what you want.

Another option is to modify a bit the architecture and use a covariant return type (it follows a minimal, working example):

template <typename T>
class Base {
    T val;

public:
    virtual const Base<T>& f(void) const {
        return *this;
    }
};

template <typename T>
class Derived: public Base<T> {
public:
    virtual const Derived<T>& f(void) const {
        return *this;
     }
};

int main(void) {
    Derived<int> B;
    Derived<int> C = B.f();
}

Which one is better for you mostly depends on the actual problem.

skypjack
  • 49,335
  • 19
  • 95
  • 187