1

我正在开发一个应用程序的设计,我想我可能会应用某种访问者设计模式,但事实证明这并不是我想要的。也许有人可以指出我在这种情况下需要的变体?

我的大部分代码都有一个模板参数“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方法派生但忘记扩展accept方法,事情就会变得糟糕Container1ContainerBase<D>更糟糕的是,我需要一个 const 和 non-const 版本的 accept 并且一些容器包含 >5 种类型,所以看起来也不好看。

ContainerBase<T>所有容器类都是通过多次继承构建的,所以我想知道是否可以使用这种结构来实现 ContainerBase 类中的接受(和接受(..)常量)?我已经看过 Lokis 类型列表,但我不知道如何在这里使用它们。你有什么主意吗?

或者是否有可能在没有访问者结构的情况下做这件事?

非常感谢!

编辑:我知道我可以使用 RTTI,但如果可能的话,我想避免运行时检查和虚拟方法。

4

3 回答 3

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 );
}
于 2010-10-09T16:34:26.527 回答
1

您可以使用 boost::mpl 定义包含类型的类型列表,如下所示:

typedef boost::mpl::vector<A, B, C> ContainedTypes;

使用 boost::mpl::for_each 您可以为这些包含的类型中的每一个调用一个仿函数。

例如

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()。这种特殊化方法的缺点是您需要将 operator() 专门化为 T 和 U 的组合。

希望这可以帮助,

马丁

于 2010-10-09T09:41:35.007 回答
0

在这种情况下,您需要的变体很可能是Boost.Variant :-)

于 2010-10-09T08:03:15.513 回答