【问题标题】:Iterate over different CRTP Derived class methods迭代不同的 CRTP 派生类方法
【发布时间】:2016-06-23 03:38:58
【问题描述】:

在下面的示例中,我有一个非常典型的 CRTP 示例,两个不同的派生类都有一个方法 bar。基类有一个方法foo,它只是转发给一些派生的bar方法

#include <iostream>

template<typename Derived>
class Base {
public:
    void foo() {
        static_cast<Derived*>(this)->bar();
    }
};

class DerivedA : public Base<DerivedA> {
public:
    void bar() {
        ::std::cout << "A\n";
    }
};

class DerivedB : public Base<DerivedB> {
public:
    void bar() {
        ::std::cout << "B\n";
    }
};

int main() {
    DerivedA a;
    DerivedB b;
    a.foo();
    b.foo();
}

似乎我不能拥有基类的数组/向量等,因为它必须具有类似于 Base&lt;T&gt; 的类型,其中 T 不同

是否存在某种没有virtual 的约定,以便能够迭代不同的派生类,假设它们都具有相同的方法(在这种情况下为bar)?

【问题讨论】:

    标签: c++ crtp


    【解决方案1】:

    您可以使用 Boost.Variant。例如:

    typedef boost::variant<DerivedA, DerivedB> Derived;
    
    struct BarCaller : public boost::static_visitor<void> {
        template <class T>
        void operator()(T& obj) {
            obj.bar();
        }
    };
    
    int main() {
        std::vector<Derived> vec{DerivedA(), DerivedB(), DerivedA()};
    
        BarCaller bar;
        for (Derived& obj : vec) {
            obj.apply_visitor(bar);
        }
    }
    

    这使您可以将异构类型存储在向量或其他 STL 容器中(通过使用“可区分联合”),并允许您对所有这些类型调用特定函数,而不管它们是否具有共同的祖先或任何虚拟方法。

    【讨论】:

    • 你会发布一个完整的例子吗?我之前没用过boost::variant
    • 我猜不会再有Base,只剩下DerivedADerivedB了?
    • @asimes:好的,如果你将它与你的类定​​义结合起来,我稍微修改了我的答案,使其成为一个完整的、可运行的程序。不管你有没有Base 都没有区别——我的代码不管怎样都可以。
    • 我将不得不在boost::variant 上做一些阅读,但谢谢,它工作得很好。我只需要了解引擎盖下发生了什么
    【解决方案2】:

    似乎我不能拥有基类的数组/向量等,因为它必须具有类似于Base&lt;T&gt; 的类型,其中T 不同。

    您可以为所有T 拥有一个Base&lt;T&gt; 基类,然后,如果适合您,您可以拥有一个指向基类的指针列表/向量/数组。

    struct BaseOne
    {
       virtual void foo() = 0;
       virtual ~BaseOne() {}
    };
    
    template<typename Derived>
    class Base : struct BaseOne {
    public:
        void foo() {
            static_cast<Derived*>(this)->bar();
        }
    };
    

    然后,

    int main() {
        std::vector<BaseOne*> v {new DerivedA, new DerivedB };
        for ( auto item : v )
           item->bar();
    
        for ( auto item : v )
           delete item;
    }
    

    是否存在某种没有virtual 的约定,以便能够迭代不同的派生类,假设它们都具有相同的方法(在这种情况下为bar)?

    不,没有。

    【讨论】:

      【解决方案3】:

      目前,variant 已成为C++17 标准的一部分,问题的解决方案可以通过std::variantstd::visit 解决,如下所示。

      示例中的模板类为Interface&lt;&gt;,并使用CRTP成语强制派生类实现helloImpl()

      #include <iostream>
      #include <vector>
      #include <variant>
      
      template<typename Implementer>
      struct Interface {
          void hello() const {
              static_cast<Implementer const *>(this)->helloImpl();
          }
      };
      

      helloImpl() 的不同实现的几个类示例

      struct Hello1 : public Interface<Hello1> {
          void helloImpl() const {
              std::cout << "Hello1" << std::endl;
          }
      };
      
      struct Hello2 : public Interface<Hello2> {
          void helloImpl() const {
              std::cout << "Hello2" << std::endl;
          }
      };
      

      下面是如何使用它将数据存储在向量容器中及其遍历:

      int main() {
          using var_t = std::variant<Hello1, Hello2>;
          std::vector<var_t> items{Hello1(), Hello1(), Hello2()};
      
          for(auto &item: items) {
              std::visit([](auto &&arg) {
                  arg.hello();
              }, item);
          }
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-09-10
        • 1970-01-01
        • 2016-06-22
        • 1970-01-01
        • 2023-03-20
        • 2014-12-01
        • 1970-01-01
        相关资源
        最近更新 更多