使用 CRTP 时的对象切片

Object slicing when using CRTP

我在使用 CRTP 时遇到对象切片问题。以下模拟说明了我的问题。

#include <memory>

class CrtpGenr
{
};

template<class t_object>
class CrtpBase : public CrtpGenr
{
    public:
        static
        auto create() -> std::unique_ptr<t_object> {
            return(std::unique_ptr<t_object>(new t_object));
        }
        void funct1(){}
};

class CrtpDirv1 : public CrtpBase<CrtpDirv1>
{
    public:
        void funct2(){}
};

class CrtpDirv2 : public CrtpBase<CrtpDirv2>
{
    public:
        void funct2(){}
};


int main()
{
/*
    // This works
    std::unique_ptr<CrtpDirv1> crtp_obj = CrtpDirv1::create();
    crtp_obj->funct1();
    crtp_obj->funct2();
*/

    std::unique_ptr<CrtpGenr> crtp_obj1 = static_cast<std::unique_ptr<CrtpGenr>>(CrtpDirv1::create());
    std::unique_ptr<CrtpGenr> crtp_obj2 = static_cast<std::unique_ptr<CrtpGenr>>(CrtpDirv2::create());
    crtp_obj1->funct1();
    crtp_obj1->funct2();

    return 0;
}

编译以上代码出现以下错误:

main.cpp: In function 'int main()':
main.cpp:47:16: error: 'class CrtpGenr' has no member named 'funct1'
 crtp_obj1->funct1();
            ^
main.cpp:48:16: error: 'class CrtpGenr' has no member named 'funct2'
 crtp_obj1->funct2();

我希望能够将 CrtpDirv1 和 CrtpDirv2 类 转换为 CrtpGenr。这样我就可以定义一个 CrtpGenr 类型的容器来保存 CrtpDirv1 或 CrtpDirv2 的对象。我做错了什么?

*crtp_obj1 属于 CrtpGenr 类型 - 正如您在第 48 行中所声明的那样。这实际上是正确的,因为整个 shebang 都源自 CrtpGenr。所以 static_casts 编译。

但是我认为你把继承的东西搞反了。看CrtpGenr的定义:

class CrtpGenr
{
};

它没有方法。为什么您希望编译器允许您尝试对该对象调用一个?!

如果你进行转换以便将一些通用基类型的对象放入容器中(例如 `std::list'),那么你可以这样做,但是当你想调用该对象的特定方法时,您需要通过将该指针转换回您创建的类型来告诉编译器它到底是什么类型的对象。

您可能想要更深入地研究继承和多态性。例如,也许 CrtpGenr class 可以有一个虚方法 funct1() 派生的 classes 实现。