迭代不同的CRTP派生类方法

时间:2016-06-23 03:38:59

标签: c++ crtp

在下面的示例中,我有一个非常典型的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<T>T不同的类型

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

3 个答案:

答案 0 :(得分:3)

  

似乎我不能拥有基类的数组/向量/等等,因为它必须有Base<T>行的类型,其中T不同。< / p>

对于所有Base<T>,您可以拥有T的基类,然后,如果适合您,您可以拥有指向基类的列表/向量/数组指针。

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)?

不,没有。

答案 1 :(得分:3)

您可以使用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容器中(通过使用&#34;区分联合&#34;),并允许您在所有这些容器上调用特定函数,而不管它们没有共同的祖先或任何虚拟方法。

答案 2 :(得分:0)

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

示例中的模板类为Interface<>,并使用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;
    }
};

以下是如何使用它将数据存储在vector <>容器中及其遍历的方法:

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;
}