我正在开发一个应用程序的设计,我想我可能会应用某种Visitor design pattern,但事实证明它并不是我想要的。也许有人可以指出我在这种情况下需要的变体?
我的大部分代码都有一个模板参数“ContainerType”,如
template <class ContainerType>
class MyClass
{
public:
void doSomething(ContainerType& container) { ... }
};
目前有一小部分但数量不断增加的“容器”通常共享许多数据字段。
template<class ContainedType>
struct ContainerBase
{
ContainedType data;
};
struct Container1: ContainerBase<A>, ContainerBase<B>
{};
struct Container2: ContainerBase<A>, ContainerBase<C>
{};
Container1和Container2现在用作MyClass(和其他)的模板参数,其中A,B,C是一些已定义的类。 (我有一些方法可以使用get<A>(container)
来访问包含的数据。这种设计提供了编译时的安全性,MyClass可以用于包含所需类型的所有容器类型。)
现在我想添加“如果容器包含某种类型(例如A),然后做某事,否则什么也不做”的功能。
这可以通过看起来像访问者的东西来完成(但请注意,没有使用虚拟方法)。它甚至允许“如果容器包含A执行此操作,如果它包含D执行其他操作,则不执行任何操作”。这可以通过
来完成template <class ContainerType>
class MyClass
{
public:
void doSomething(ContainerType& container)
{
container.accept(*this);
}
void visit(B& b){...}
void visit(D& d){...}
template<typename T>
void visit(T& t){}
};
struct Container1: ContainerBase<A>, ContainerBase<B>
{
template<class T>
void accept(T& t)
{
t.visit(ContainerBase<A>::data);
t.visit(ContainerBase<B>::data);
}
};
这就是我想要的,但我正在寻找一种更好的方法,因为这里显示的实现需要为每个ContainerType实现accept。如果有人来自Container1
和ContainerBase<D>
但忘记扩展接受方法,那么事情就会变得糟糕。更糟糕的是,我需要一个const和非const版本的accept,而一些容器包含&gt; 5种类型,所以看起来也不一样。
所有容器类都是通过多次从ContainerBase<T>
继承来构建的,所以我想知道我是否可以使用这个结构在ContainerBase类中实现accept(和accept(..)const)?我已经看过Lokis类型列表,但我不知道如何在这里使用它们。你有什么想法吗?
或者没有类似访客的结构可以做这件事吗?
非常感谢!
编辑:我知道我可以使用RTTI,但如果可能的话,我想避免运行时检查和虚拟方法。答案 0 :(得分:3)
如果你可以改变容器类的定义方式,看起来很容易用Boost.Fusion实现
例如
#include <iostream>
#include <boost/fusion/container/vector.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
struct A {};
struct B {};
struct C {};
namespace fusion = boost::fusion;
struct Container1 : fusion::vector< A, B > {};
struct Container2 : fusion::vector< A, C > {};
struct doSomethingWithData {
void operator()( B &b ) const
{
std::cout << "do something with B" << std::endl;
}
void operator()( C &c ) const
{
std::cout << "do something with C" << std::endl;
}
template < typename T >
void operator()( T &t ) const
{
std::cout << "Neither B nor C" << std::endl;
}
};
template < typename ContainerType >
void doSomething( ContainerType &container )
{
fusion::for_each( container, doSomethingWithData() );
}
int main()
{
Container1 c1;
doSomething( c1 );
std::cout << "----------------------" << std::endl;
Container2 c2;
doSomething( c2 );
}
答案 1 :(得分:1)
您可以使用boost :: mpl来定义包含类型的类型列表,如下所示:
typedef boost::mpl::vector<A, B, C> ContainedTypes;
使用boost :: mpl :: for_each,您可以为每个包含的类型调用一个仿函数。
e.g。
template<class U>
class Visitor
{
public:
Visitor(MyClass<U>& item) : _item(item)
{}
template<class T>
void operator() (T)
{
// Do what ever you want, this may be specialized as needed
}
private:
MyClass<U>& item;
}
然后致电
boost::mpl::for_each<ContainedTypes> (Visitor(MyClass<ContainerType>& item))
这将为ContainedTypes中的每个类调用Visitor的operator()。 这种专业化方法的缺点是你需要为T和U的组合专门化operator()。
希望这有帮助,
马丁
答案 2 :(得分:0)
在这种情况下您需要的变体可能是Boost.Variant: - )