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?
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()
.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments