迭代不同的 CRTP Derived class 方法
Iterate over different CRTP Derived class methods
在下面的示例中,我有一个非常典型的 CRTP 示例,两个不同的派生 classes 都有一个方法 bar
。基础 class 有一个方法 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();
}
我似乎无法拥有基数 class 的数组/向量等,因为它必须具有符合 Base<T>
的类型,其中 T
不同
是否有某种没有 virtual
的约定能够迭代不同的派生 classes 假设它们都具有相同的方法(bar
在这种情况下)?
It doesn't seem like I can have an array / vector / etc. of the base class because it would have to have a type along the lines of Base<T>
where T
is different.
对于所有 T
,你可以有一个 Base<T>
的基 class,然后,你可以有一个 list/vector/array 指向基 class 的指针,如果这对你有用。
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;
}
Is there some kind of convention without virtual
for being able to iterate over different derived classes assuming they all have the same method (bar
in this case)?
没有,没有。
您可以使用 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 容器中(通过使用 "discriminated union"),并允许您对所有这些类型调用特定函数,而不管它们是否没有共同的祖先或任何虚拟方法。
截至目前,变体已成为C++17
标准的一部分,问题的解决方案可以通过std::variant
和std::visit
解决,如下所示。
示例中的模板class是Interface<>
并使用CRTP惯用语强制派生class实现helloImpl()
:
#include <iostream>
#include <vector>
#include <variant>
template<typename Implementer>
struct Interface {
void hello() const {
static_cast<Implementer const *>(this)->helloImpl();
}
};
几个 class 示例,具有 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;
}
在下面的示例中,我有一个非常典型的 CRTP 示例,两个不同的派生 classes 都有一个方法 bar
。基础 class 有一个方法 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();
}
我似乎无法拥有基数 class 的数组/向量等,因为它必须具有符合 Base<T>
的类型,其中 T
不同
是否有某种没有 virtual
的约定能够迭代不同的派生 classes 假设它们都具有相同的方法(bar
在这种情况下)?
It doesn't seem like I can have an array / vector / etc. of the base class because it would have to have a type along the lines of
Base<T>
whereT
is different.
对于所有 T
,你可以有一个 Base<T>
的基 class,然后,你可以有一个 list/vector/array 指向基 class 的指针,如果这对你有用。
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;
}
Is there some kind of convention without
virtual
for being able to iterate over different derived classes assuming they all have the same method (bar
in this case)?
没有,没有。
您可以使用 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 容器中(通过使用 "discriminated union"),并允许您对所有这些类型调用特定函数,而不管它们是否没有共同的祖先或任何虚拟方法。
截至目前,变体已成为C++17
标准的一部分,问题的解决方案可以通过std::variant
和std::visit
解决,如下所示。
示例中的模板class是Interface<>
并使用CRTP惯用语强制派生class实现helloImpl()
:
#include <iostream>
#include <vector>
#include <variant>
template<typename Implementer>
struct Interface {
void hello() const {
static_cast<Implementer const *>(this)->helloImpl();
}
};
几个 class 示例,具有 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;
}