在下面的示例中,我有一个非常典型的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
)?
答案 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::variant
和std::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;
}