C++ CRTP clonable类的协变类型无效

C++ CRTP clonable类的协变类型无效,c++,multiple-inheritance,crtp,cloneable,covariant,C++,Multiple Inheritance,Crtp,Cloneable,Covariant,我正试图用CRTP实现一个可关闭的类。但是,我需要有一个抽象类,它有一个由子类重写的纯虚拟克隆方法。为了实现这一点,我需要克隆函数返回协变返回类型。我在下面编写了这段代码,编译器向我大喊这个错误: main.cpp:12:5: error: return type of virtual function 'clone' is not covariant with the return type of the function it overrides ('B *' is not derived

我正试图用CRTP实现一个可关闭的类。但是,我需要有一个抽象类,它有一个由子类重写的纯虚拟克隆方法。为了实现这一点,我需要克隆函数返回协变返回类型。我在下面编写了这段代码,编译器向我大喊这个错误:

main.cpp:12:5: error: return type of virtual function 'clone' is not covariant with the return type of the function it overrides ('B *' is not derived from 'AbstractClonable *')
类“B”似乎是AbstractClonable的子类,甚至是双向的!我怎样才能解决这个问题?非常感谢你。我尝试了使用Clang3.6和GCC4.9.2

struct AbstractClonable {
    virtual AbstractClonable* clone() const = 0;
};

template<typename T>
struct Clonable : virtual AbstractClonable {
    T* clone() const override {
        return new T{*dynamic_cast<const T*>(this)};
    }
};

struct A : virtual AbstractClonable {

};

struct B : A, Clonable<B> {

};
struct AbstractClonable{
虚拟抽象克隆*clone()常量=0;
};
模板
结构可克隆:虚拟抽象可克隆{
T*clone()常量重写{
返回新的T{*dynamic_cast(this)};
}
};
结构A:虚拟抽象可克隆{
};
结构B:A,可克隆{
};

我认为问题在于

T*clone()常量覆盖{
返回新的T{*dynamic_cast(this)};
}

返回B*而不是AbstractClonable*

我认为问题在于

T*clone()常量覆盖{
返回新的T{*dynamic_cast(this)};
}

返回B*,而不是AbstractClonable*

,即使
B
确实是从
Clonable
派生出来的,这里的问题是
Clonable
构造按照其定义是无效的

B* clone() const override
当然,这不是对
AbstractClonable::clone()
的重写,因为编译器此时不会将
B
视为
AbstractClonable
的子级。因此,我认为问题在于编译器无法构建
B
Clonable
基础

解决方法(但实际上与您想要的不同)是定义

Clonable* clone() const override
Clonable
中。正如您在评论中提到的,您还可以定义一个自由函数

template<typename T> 
T* clone(const T* object) 
{ 
    return static_cast<T*>(object->clone()); 
}
模板
T*克隆(常数T*对象)
{ 
返回静态_cast(对象->克隆());
}

相关:

即使
B
确实来自
Clonable
,这里的问题是
Clonable
构造是无效的,正如它定义的那样

B* clone() const override
当然,这不是对
AbstractClonable::clone()
的重写,因为编译器此时不会将
B
视为
AbstractClonable
的子级。因此,我认为问题在于编译器无法构建
B
Clonable
基础

解决方法(但实际上与您想要的不同)是定义

Clonable* clone() const override
Clonable
中。正如您在评论中提到的,您还可以定义一个自由函数

template<typename T> 
T* clone(const T* object) 
{ 
    return static_cast<T*>(object->clone()); 
}
模板
T*克隆(常数T*对象)
{ 
返回静态_cast(对象->克隆());
}

相关:

是的,
B
是从
AbstractClonable
派生的,但是编译器在
Clonable
的实例化过程中不知道这一点,因为
B
在那一点上仍然不完整

C++14§10.3/8:

如果
D::f
的协变返回类型中的类类型与
B::f
的类类型不同,则
D::f
的返回类型中的类类型应在
D::f
的声明点处完成,或应为
D
的类类型

类具有在协变返回类型中使用自身的特殊权限。其他类,包括CRTP基,在声明协变函数之前需要等待该类完成

您可以使用非虚拟接口习惯用法(NVI)解决此问题:

类抽象可克隆{
受保护的:
虚拟抽象克隆*do_clone()常量=0;
公众:
AbstractClonable*克隆()常量{
返回do_clone();
}
};
模板
类Clonable:公共虚拟抽象Clonable{
Clonable*do_clone()const override{//避免在此声明中使用T。
返回新的T{*dynamic_cast(this)};
}
公众:
T*clone()常量{//但是在这里,它是可以的。
返回static_cast(do_clone());
}
};

是的,
B
是从
AbstractClonable
派生出来的,但是编译器在
Clonable
的实例化过程中不知道这一点,因为
B
在这一点上仍然是不完整的

C++14§10.3/8:

如果
D::f
的协变返回类型中的类类型与
B::f
的类类型不同,则
D::f
的返回类型中的类类型应在
D::f
的声明点处完成,或应为
D
的类类型

类具有在协变返回类型中使用自身的特殊权限。其他类,包括CRTP基,在声明协变函数之前需要等待该类完成

您可以使用非虚拟接口习惯用法(NVI)解决此问题:

类抽象可克隆{
受保护的:
虚拟抽象克隆*do_clone()常量=0;
公众:
AbstractClonable*克隆()常量{
返回do_clone();
}
};
模板
类Clonable:公共虚拟抽象Clonable{
Clonable*do_clone()const override{//避免在此声明中使用T。
返回新的T{*dynamic_cast(this)};
}
公众:
T*clone()常量{//但是在这里,它是可以的。
返回static_cast(do_clone());
}
};

是的,它返回一个B*,这是我的目标!因为B继承自AbstractClonable,所以它们不应该是有效的协变类型吗?因为从B*到AbstractClonable*转换涉及多个虚拟继承,所以需要发出额外的代码。此代码将使用指向虚拟基类的指针执行基类查找。若使用指向AbstractClonable编译器的指针调用clone()方法,则可能无法正确执行类型转换。所以我不认为B*和AbstractClonable*是协变的。是的,它返回一个B*,这是我的目标!因为B继承自AbstractClonable,所以它们不应该是有效的协变类型吗?因为存在多个虚拟继承