C++ C++;派生类的CRTP构造函数

C++ C++;派生类的CRTP构造函数,c++,constructor,crtp,C++,Constructor,Crtp,我对CRTP有一些问题。假设我有以下代码 #include <iostream> // interface template<class Imp> class Interface{ public: inline void print(void){ std::cout<<"value: "; return asImp().print(); } private: typedef Imp Implement

我对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();
}
因此,变量
似乎由默认构造函数构造为0。但我不明白在什么地方和什么时候调用这个构造函数,因为没有创建派生类的对象

此外,假设我想用不同的起始值来创造价值,那么如何使用这种设计模式来实现这一点呢? 显然,我可以在派生类中创建一个
init()
方法,该方法在基类的构造函数中调用,但对于没有默认构造函数的类型,它不起作用


最后,是否可以将一些传递给基类构造函数的参数转发给派生类的构造函数?

您实际上不是在创建派生对象

您正在创建一个基类对象并将其强制转换为派生类型的引用,但底层对象仍然是基类对象,因此它是错误的

static\u cast
始终成功,但如果您没有将其转换为正确的类型,则会引发未定义的行为,而
dynamic\u cast
如果您将其转换为错误类型,则会返回空指针

->在此处尝试live,您可能会或可能不会获得垃圾值:

一旦解决了上述问题,另一个初始化问题应该很简单


更多信息:

您实际上不是在创建派生对象

您正在创建一个基类对象并将其强制转换为派生类型的引用,但底层对象仍然是基类对象,因此它是错误的

static_ucast
始终成功,但如果不转换到正确类型,则会引发未定义的行为,而如果抛出错误,则返回空指针

->在此处尝试live,您可能会或可能不会获得垃圾值:

一旦解决了上述问题,另一个初始化问题应该很简单


更多信息:

在源代码中,您自己将基类命名为
接口
。在面向对象的意义上,您不创建
接口的实例
,而是创建从
接口
派生的类的实例。主要代码错误地实例化了
接口
,而不是从该接口派生的类。您可以通过强制
接口
使用使其无法实例化的属性来解决此问题。例如:

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

现在,只要派生类提供了
wait()
perform()

的适当实现,那么从
WorkerInterface
派生的任何类都将提供一个
work()
方法,该方法将做“正确”的事情。在源代码中,您自己将基类命名为
Interface
。在面向对象的意义上,您不创建
接口的实例
,而是创建从
接口
派生的类的实例。主要代码错误地实例化了
接口
,而不是从该接口派生的类。您可以通过强制
接口
使用使其无法实例化的属性来解决此问题。例如:

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

现在,从
WorkerInterface
派生的任何类都将提供一个
work()
方法,只要派生类提供了
wait()
perform()的合适实现,该方法就会做“正确”的事情

我认为这里发生的是,您正在将
静态\u转换为
foo
添加到
Add
,而不进行运行时检查。然后,它将在找到
值的位置解释数据,就像它是原始结构的一部分一样(
接口
)。它恰好是0。这是未定义的行为,您正在访问未初始化的
Add
部分(您将基指针强制转换为派生指针,但仅构造基部分,然后访问派生部分)。例如,我在
g++
value:-466819072
value:-466819071
确定下得到垃圾。那么,有没有一种方法可以将变量存储在派生类中?或者,唯一的解决方案是将其放在基类中?您需要
addfooinst。然后,
Interface&foo=fooinst
@jxh据我所知,在这种设计模式中,不应该创建派生类(它就像一个策略)。我认为这里发生的事情是,您正在将
foo
强制转换为
Add
,它不进行运行时检查。然后,它将在找到
值的位置解释数据,就像它是原始结构的一部分一样(
接口
)。它恰好是0。这是未定义的行为,您正在访问未初始化的
Add
部分(您将基指针强制转换为派生指针,但仅构造基部分,然后访问派生部分)。例如,我在
g++
value:-466819072
value:-466819071
确定下得到垃圾。那么,有没有一种方法可以将变量存储在派生类中?或者,唯一的解决方案是将其放在基类中?您需要
addfooinst。然后,
Interface&foo=fooinst
@jxh据我所知,在这个设计模式中,不应该创建派生类(它就像一个策略)。因此,既然没有创建派生对象,为什么变量值存在?我预计会出现错误。@Marcagnese您铸造了指针,因此
C++
具有派生对象的内存布局,但后者未初始化。这与说
(派生*)p
相同,其中
p
只是指向某个内存的指针。实际上,你将能够编写
p->member
,但这当然毫无意义。@marcagnese的意思是
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();
}