关于c ++:与CRTP clonable类无关的协变类型

Invalid covariant type with CRTP clonable class

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

1
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"似乎是可抽象的子类,甚至是双向的!我怎么解决这个问题?非常感谢你。我试过使用Clang 3.6和GCC 4.9.2。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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是从AbstractClonable派生出来的,但是编译器不知道在Clonable的实例化过程中,因为B在这一点上仍然是不完整的。

C++ 14×103/8:

If the class type in the covariant return type of D::f differs from that of B::f, the class type in the return type of D::f shall be complete at the point of declaration of D::f or shall be the class type D.

类具有在协变返回类型中使用自身的特殊权限。其他类,包括crtp基,需要等到类完成后再声明协变函数。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class AbstractClonable {
protected:
    virtual AbstractClonable* do_clone() const = 0;
public:
    AbstractClonable *clone() const {
        return do_clone();
    }
};

template<typename T>
class Clonable : public virtual AbstractClonable {
    Clonable* do_clone() const override { // Avoid using T in this declaration.
        return new T{*dynamic_cast<const T*>(this)};
    }
public:
    T *clone() const { // But here, it's OK.
        return static_cast< T * >( do_clone() );
    }
};

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

1
B* clone() const override

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

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

1
Clonable* clone() const override

Clonable中。正如您在注释中提到的,您还可以定义一个自由函数

1
2
3
4
5
template<typename T>
T* clone(const T* object)
{
    return static_cast<T*>(object->clone());
}

相关:派生出奇怪的循环模板和协方差


我想问题是

1
2
3
T* clone() const override{
    return new T{*dynamic_cast<const T*>(this)};
}

返回b*而不是abstractClonable*。