2

请考虑这个 - 可能写得不好 - 的例子:

class Command;

 class Command : public  boost::enable_shared_from_this<Command>
 { 
  public :
   void execute()
   { 
    executeImpl();
                // then do some stuff which is common to all commands ... 
   }

   // Much more stuff ...
     private:
      virtual void executeImpl()=0;
         // Much more stuff too ...
 };

和 :

class CmdAdd : public Command
 {
 public:
  CmdAdd(int howMuchToAdd);
  void executeImpl();


  int _amountToAdd;
 };

// implementation isn't really important here .... 

有了这个,我可以使用以下语法简单地添加一个回调:

        boost::shared_ptr<Command> cmdAdd(CmdAdd(someValue));
     cmdAdd->execute();

它完美无缺。我的“命令”类做了更多所有命令共有的事情,例如实现撤消、重做、进度报告等,但为了便于阅读,我将其从代码中删除。

现在我的问题很简单:有没有办法重写命令类,以便我可以替换这个调用:

boost::shared_ptr<Command> cmdAdd(CmdAdd(someValue));
cmdAdd->execute();

通过类似的东西:

CmdAdd(someValue); // preferably
or CmdAdd->execute(someValue)

我一直在考虑这个问题,但我有一个概念上的问题:我想模板化我的 Command 类,比如

template <typename R,typename T1, typename T2, ..., typename Tn> class Command
{
    R1 execute(T1 p1, ...,Tn pn)
    { 
        return executeImpl(T1 p1, ...,Tn pn);
        // then do some stuff which is common to all commands ... 
    }
}

但显然,这里有一个问题:语法template <typename R,typename T1, typename T2, ..., typename Tn>不合法 C++ ,AFAIK。

我是否必须编写 n 个版本的命令,例如:

template <typename R> class Command
template <typename R,typename T1> class Command
template <typename R,typename T1, typename T2> class Command
...

等等 ?(甚至不确定这是否真的有效)

还是有另一种更优雅的方式来做到这一点?这里提到的语法在那里有用吗?(函数 f;)

我一直在查看 Loki 的类型列表,他们似乎完成了这项工作。但是我在Boost中找不到任何东西。我在网上看到 boost::mpl 是一个想要用来实现类型列表的工具,但我对 MPL 文档有点困惑?

对此有何见解?雷加兹,D.

4

4 回答 4

2

AFAIK 你不能用当前的 C++ 标准真正做到这一点。一些 boost 代码使用宏和其他预处理来模拟可变参数模板(我认为 boost::pool 或 boost::object_pool 使用类似的东西)。

但是,可变参数模板将出现在下一个标准 C++0x 中,根据此页面,GCC 已经提供了从 v4.3 开始的实现:http: //gcc.gnu.org/projects/cxx0x.html

如果您正在使用它,您可以通过激活 C++0x 来启用它。

于 2010-06-27T02:26:30.207 回答
2

有趣的问题:)

首先,您忽略了一个问题:您需要一个通用的基类,Command如果您要使用它们的堆栈(用于撤消/重做),则无法对此类进行模板化。

因此,您被困在:

class Command
{
public:
  void execute(); 
private:
  virtual void executeImpl() = 0;
};

我可以理解您对带有参数的执行函数的需求,但不要忘记无论如何您都需要保存这些参数以进行撤消/重做操作。通过构造函数获取它们更简单。

但是,您仍然可以使用模板化方法来实际调用命令:

template <class Command>
void execute() { Command cmd; cmd.execute(); }

template <class Command, class T0>
void execute(T0& arg0) { Command cmd(arg0); cmd.execute(); }

/// ...

int main(int argc, char* argv[])
{
  execute<MyLittleCommand>("path", 3);
}

这与您想要的语法很接近。请注意,我故意忘记了这里的堆栈,在我看来,您需要将其传递给execute注册方法(一旦完成)。

并不是说我也可能会更改Command设计以更接近策略模式:

struct CommandImpl
{
  virtual ~CommandImpl();
  virtual void executeImpl() = 0;
};

class Command
{
public:
  template <class C>
  static Command Make() { return Command(new C()); }

  template <class C, class T0>
  static Command Make(T0& arg0) { return Command(new C(arg0)); }

  /// ....

  void execute(CommandStack& stack)
  {
    mImpl->executeImpl();
    stack.Push(*this);
  }

private:
  Command(CommandImpl* c): mImpl(c) {}
  boost::shared_ptr<CommandImpl> mImpl;
};

它是非虚拟接口和指向实现习语的典型组合。

于 2010-06-27T12:39:35.153 回答
2

乍一看,可变参数模板似乎是完美的解决方案。不幸的是,它们不能很好地处理虚函数:

template <typename... Args>
void execute(Args&&... args)
{
    executeImpl(std::forward<Args>(args)...);
}

这需要executeImpl是一个成员函数模板,但是C++中没有这个东西!

于 2010-06-27T09:10:00.853 回答
0

正如 Klaim 所指出的,可变参数模板是这个问题的最终解决方案。但是,有一种方法可以使用类型列表允许可变数量的模板参数:

template <class H, class T>
struct typelist
{
    typedef H head;
    typedef T tail;
};

例如,这允许您编写typelist<typelist<int, float>, double>。然而,阅读和写作确实让人头疼,这也是 boost::function 使用蛮力方法的主要原因(每个模板参数的单独类): boost::function0, boost::function1 , boost::function2 等用于其后端实现。这比通过模板元编程递归地遍历类型列表要容易得多。

至于一般性答案,我将其发布在您最初遇到此问题的另一个线程中以及另一个问题中。

于 2010-06-27T05:13:59.440 回答