【问题标题】:A polymorphic collection of Curiously Recurring Template Pattern (CRTP) in C++?C ++中奇怪重复模板模式(CRTP)的多态集合?
【发布时间】:2022-01-01 22:45:36
【问题描述】:

我有一个类Base,其中有两个类,DerivedADerivedB,定义如下。

template <typename Derived>
class Base{
public:
    double interface(){
        static_cast<Derived*>(this)->implementation();
    }
};

class DerivedA : public Base<DerivedA>{
public:
    double implementation(){ return 2.0;}
};

class DerivedB : public Base<DerivedB>{
public:
    double implementation(){ return 1.0;}
};

简而言之,我正在尝试执行以下操作来维护对象集合,其中一些是DerivedA,其中一些是DerivedB

std::vector<std::shared_ptr<Derived>>

这显然是不可能的,因为我现在已将 Derived 类设为模板类。

有什么方法可以创建/维护对象的多态集合?

编辑:不幸的是,一个简单的模板化结构不起作用,因为函数implementation 在我的实际程序中是模板化的——所以implementation 必须是一个模板化的纯虚函数,这是不可能的。请原谅我缺乏解释。

【问题讨论】:

  • -1 显示的代码在语法上无效(例如Class
  • Derived 不是您的伪代码中的类型
  • 抱歉,修复了我的示例中的一连串拼写错误。当前(更新)的帖子格式正确。我没有模板元编程可以做吗?
  • @quantdev 我认为他的意思是Base。用户:你为什么不能放弃 CRTP 并使用virtual 函数?请不要回复purrformance
  • @Galik 你不能有一个模板化的(纯)虚函数。

标签: c++ inheritance c++11 polymorphism crtp


【解决方案1】:

此答案与当时的问题有关。


不要使用不是动态多态的CRTP来创建动态多态。

使用虚函数。

这就是他们的目的。

class Base
{
private:
    virtual
    auto implementation() -> double = 0;

public:
    auto interface() -> double { return implementation(); }
};

class DerivedA
    : public Base
{
private:
    auto implementation() -> double override { return 2.0; }
};


class DerivedB
    : public Base
{
private:
    auto implementation() -> double override { return 1.0; }
};

【讨论】:

  • 不幸的是,我需要一个模板化的纯虚函数——这在 C++ 标准中是不允许的。
  • @user2899162:没有办法对您的额外要求投反对票:(
  • 我知道,对不起,我想得比打字快得多。
【解决方案2】:

阿尔夫的建议是正确的。它很容易适应您的额外要求。用纯虚方法定义接口:

struct BaseInterface {
    virtual ~BaseInterface() {}
    virtual double interface() = 0;
};

现在,您的模板基类可以从接口派生:

template <typename Derived>
class Base : BaseInterface {
public:
    double interface(){
        static_cast<Derived*>(this)->implementation();
    }
};

现在,您可以创建一个指向接口的指针向量:

std::vector<std::shared_ptr<BaseInterface>>

【讨论】:

  • 哇,尽管我对我的问题的解释很糟糕(我正在重写我的问题,以便至少可以理解......)你给出了我正在寻找的确切答案。非常感谢!
  • 不客气。牢记 Alf 的建议:在适当的时候使用多态类型。
  • Base 到底在做什么?为什么Derived不能直接继承自BaseInterface
  • @n.m.:通常,基类不知道派生自什么类型。 CRTP 允许基类拥有该知识。 Base 正在使用 CRTP,同时也继承自 BaseInterface。如果Derived直接继承自BaseInterface,则无法调用implementation()
  • @n.m.:请与 Jim Coplien 讨论一下(明确一点,我不是他)。
【解决方案3】:

因为Base&lt;DerivedA&gt;Base&lt;DerivedB&gt; 相比是一个完全不同的类型,所以你是对的,你不能只做std::vector&lt;std::shared_ptr&lt;Base&gt;&gt; 这样的事情,因为它在语法上是无效的,并且对于C++ 没有有意义的语义。

实现您想要的保留当前 CRTP 层次结构的一种方法是创建一个类型擦除接口(或者它应该被称为什么?我不确定......)。它基本上是一个包装器,它定义了一个特定的接口,您可以在其中包装遵循该接口的对象。

#include <vector>
#include <memory>
#include <iostream>


class VirtualBase {  // Really am not sure what it should be called, sorry
    class Interface {
    public:
        virtual ~Interface() = default;
        virtual double get() = 0;
    };

    template<typename T>
    class Impl : public Interface {
        T m_impl_obj;

    public:
        Impl(T impl_obj) : m_impl_obj(std::move(impl_obj)) {}

        double get() override {
            return m_impl_obj.get();
        }
    };

    std::shared_ptr<Interface> m_obj;

public:
    template<typename T>
    VirtualBase(T obj) : m_obj(new Impl<T>(std::move(obj))) {}

    double get() {
        return m_obj->get();
    }
};


template <typename Derived>
class Base{
public:
    double get(){
        return static_cast<Derived*>(this)->implementation();
    }
};

class DerivedA : public Base<DerivedA>{
public:
    double get(){ return 2.0;}
};

class DerivedB : public Base<DerivedB>{
public:
    double get(){ return 1.0;}
};


int main() {
    std::vector<VirtualBase> v;
    v.emplace_back(DerivedA{});
    v.emplace_back(DerivedB{});

    for(auto b : v) {
        std::cout << b.get() << std::endl;
    }
}

Live example

这很不完整,但它应该可以工作,至少在我需要这样的设计的情况下。 Sean Parent 在 GoingNative 2013 上的演讲:http://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil 是一个很好的介绍,并解释了如何以及为什么这样做。真的,您应该看到它,包括 GoingNative 中的所有其他精彩演示。

【讨论】:

    【解决方案4】:

    编辑:这是基于 OP 实际上需要虚函数模板的假设。显然不是这样的。


    不可能有虚函数模板。

    部分模拟它们的一种方法是访问者模式。它通常用于向现有类添加动态分派的功能。这种添加的功能可以模板化这一事实经常被忽视。

    伪代码示例

    class Base
    {
        virtual void accept (Visitor*) = 0;
    }
    
    class Derived1 : public Base
    {
        void accept(Visitor* v) { v->visit(this); }
    }
    

    类 Derived2:公共基础 { void accept(Visitor* v) { v->visit(this); } }

    class Visitor
    {
         virtual void visit (Derived1*) = 0;
         virtual void visit (Derived2*) = 0;
    }
    
    template<class X, class Y>
    class FooVisitor
    {
       X x; Y y;
       FooVisitor(X x, Y y): x(x), y(y) {}
    
         void visit (Derived1*) { x.func(y); }
         void visit (Derived2*) { y.otherfunc(x); }
      }
    

    当然,Visitor 的所有缺点也都在这里。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-15
      • 2019-11-18
      相关资源
      最近更新 更多