我对 CRTP 有一些疑问。假设我有以下代码
#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();
}
输出是
value: 0
value: 1
因此,变量 value
似乎被默认构造函数构造为 0。但是我不明白这个构造函数是在何时何地被调用的,因为没有创建派生类的对象。
此外,假设我想用不同的起始值创造值(value),我如何使用这种设计模式来实现?
显然,我可以在派生类中创建一个 init()
方法,该方法在基类的构造函数中调用,但它不适用于没有默认构造函数的类型。
最后,是否可以将传递给基类构造函数的一些参数转发给派生类的构造函数?
最佳答案
在您的源代码中,您自己将基类命名为Interface
。在面向对象的意义上,您创建的不是 Interface
的实例,而是从 Interface
派生的类的实例。您在 main 中的代码错误地实例化了一个 Interface
而不是从它派生的类。您可以通过强制 Interface
具有使其无法实例化的属性来解决此问题。例如:
template<class Imp>
class Interface {
//...
protected:
Interface () {} // not accessible except by derived
};
与常规多态性不同,您并不是真的希望传递 Interface
对象。 Interface
通过提供预期的方法来强制执行特定接口(interface),而那些从 Interface
派生的方法必须采用该接口(interface)的预期。您的示例有点做作,因为 Interface
实际上只是分派(dispatch)到派生中的相同命名方法。但是,一个更好的例子是 Interface
使用它期望派生类型提供的属性来提供实现。
在下面的例子中,我们看到Worker
继承了WorkerInterface
的接口(interface)。但是,由于WorkerInterface
的期望,需要实现perform()
和wait()
。纯接口(interface)通过纯虚方法强制执行此要求,而 CRTP 通过模板扩展强制执行此要求。
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();
}
现在,任何派生自 WorkerInterface
的类都将提供一个 work()
方法,只要派生类提供合适的实现,该方法就会做“正确”的事情wait()
和 perform()
的组合。
关于派生类的 C++ CRTP 构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25083574/