0

I have some questions about the CRTP. Let's say I have the following code

#include <iostream>

// interface

template<class Imp>
class Interface{
  public:
    inline void print(void){
      std::cout<<"value: ";
      return asImp().print();
    }
  private:
    typedef Imp Implementation;
    inline Implementation& asImp(void){return static_cast<Implementation&>(*this);}
};

// add
class Add:public Interface<Add>{
  public:
    inline void print(void){std::cout<<value<<std::endl;++value;}
  private:
    int value;
};

// main
int main(void){
  Interface<Add> foo;
  foo.print();
  foo.print();
}

The output is

value: 0
value: 1

Therefore the variable value seems to be constructed as 0 by the default constructor. But I don't understand where and when this constructor is called since no object of the derived class is created.

Moreover, let's suppose that I want create value with a different starting value, how can I achieve that using this design pattern? Obviously I could create an init() method in the derived class which is called in the constructor of the base class but it wouldn't work for a type which hasn't a default constructor.

Finally, is it possible to forward some arguments pass to the constructor of the base class to the constructor of the derived class?

Marco Agnese
  • 349
  • 4
  • 15
  • I think what's going on here is that you're `static_cast`ing your `foo` to an `Add`, which does no runtime check. It's then interpreting the data at the place where it would find `value` as though it were part of the original structure (`Interface`). It just happens to be 0. – Jared Windover-Kroes Aug 01 '14 at 15:34
  • It is undefined behaviour, you are accessing the part of `Add` that is not initialized (you cast a base pointer to a derived pointer, but only the base part is constructed, then access the derived part). For example, I get junk under `g++`, `value:-466819072` and `value: -466819071` – vsoftco Aug 01 '14 at 15:35
  • Ok. So is there a way to have variables stored in the derived class? Or the only solution is to have it in the base class? – Marco Agnese Aug 01 '14 at 15:41
  • You want `Add fooinst;`. Then, `Interface &foo = fooinst;`. – jxh Aug 01 '14 at 15:43
  • @jxh for what I understand, in this designed pattern, the derived class shouldn't be created (it is like a policy). – Marco Agnese Aug 01 '14 at 15:50
  • That would be incorrect. – jxh Aug 01 '14 at 16:11

2 Answers2

2

You're NOT creating a derived object in fact.

You're creating a base class object and casting it to a reference of the derived type, but the underlying object is still a base class one and thus it's wrong.

static_cast always succeeds, but will raise undefined-behavior if you don't cast to the right type while dynamic_cast returns a NULL pointer if you casted it wrong.

-> Try it live here, you might or might not get garbage values: http://ideone.com/dP3jjU

The other initialization question should be straightforward once you've addressed the above.

More on this: Should static_cast<Derived *>(Base pointer) give compile time error?

Community
  • 1
  • 1
Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • Therefore, since no derived object is created, why does the variable value exists? I would expect an error. – Marco Agnese Aug 01 '14 at 15:40
  • @MarcoAgnese you casted the pointer, so `C++` has the memory layout for a derived object, but the latter is not-initialized. It is the same as saying `(Derived*) p`, where `p` is just some pointer to some memory. Sintactically you will be able to write `p->member`, but of course it will make no sense. – vsoftco Aug 01 '14 at 15:41
  • @MarcoAgnese [Undefined behavior](http://en.wikipedia.org/wiki/Undefined_behavior) means that anything could happen. You're likely accessing memory owned by your own process (thus no access violation) but reading garbage, uninitialized, unset, values. – Marco A. Aug 01 '14 at 15:42
  • @Marco A. Yes got it, for this reason I was asking if there is a proper way to construct variables in the derived class. But since, it is never constructed, I think that the answer is NO. Therefore, is there a design pattern to have static polyphormism and variables in the derived class? – Marco Agnese Aug 01 '14 at 15:53
  • 1
    One of the points of [CRTP](http://stackoverflow.com/q/262254/1938163) is to be able to do static polymorphism and know the type of the derived object at compile-time. If you create a derived object you can simply initialize the variables you need there, unless I'm missing your point – Marco A. Aug 01 '14 at 15:57
  • I will try to do an example. Let's say that I want to create 2 derived classes: AddOne and AddTwo. I want that an object Interface has value constructed to be 0 while if I create an object Interface the value is constructed as 1. Is there a way? I suppose that there isn't if I understood correctly your explanation – Marco Agnese Aug 01 '14 at 16:04
  • You're missing one point: **you create derived objects**, not base ones. Take a look at here: http://ideone.com/n9Ol72 – Marco A. Aug 01 '14 at 16:37
2

In your source code, you yourself name the base class as Interface. In the object oriented sense, you do not create instances of an Interface, but instances of classes that derive from the Interface. Your code in main erroneously instantiates an Interface rather than the class that derived from it. You can fix this by forcing Interface with properties that make it impossible to instantiate. E.g.:

template<class Imp>
class Interface {
    //...
protected:
    Interface () {} // not accessible except by derived
};

Unlike regular polymorphism, you are not really expected to pass Interface objects around. The Interface provides enforcement of a particular interface by providing the expected methods, and those that derive from the Interface must adopt the expectations of that interface. Your example is a little contrived because the Interface is actually just dispatching to the same named method in the derived. But, a better example is one where the Interface provides an implementation using properties it expects the derived type to provide.

In the example below, we see that Worker inherits the interface of WorkerInterface. But, because of the expectations of WorkerInterface, it is required to implement perform() and wait(). While a pure interface enforces this requirement with pure virtual methods, CRTP enforces this with template expansion.

template <typename JOB>
class WorkerInterface {
public:
    void work () { while (job().wait()) job().perform(); }
private:
    JOB & job () { return *static_cast<JOB *>(this); }
protected:
    WorkerInterface () {}
};

class Worker : public WorkerInterface<Worker>
{
    friend class WorkerInterface<Worker>;
    int state_;
    void perform () { std::cout << "Worker: " << __func__ << '\n'; }
    bool wait () { return state_--; }
public:
    Worker () : state_(1) {}
};

int main ()
{
    Worker w;
    w.work();
}

Now, any class that derives from WorkerInterface will be provided a work() method that will do the "right" thing as long as the derived class provides suitable implementations of wait() and perform().

jxh
  • 69,070
  • 8
  • 110
  • 193