拥有可变数量的模板参数的最佳方法是什么?

时间:2010-06-27 01:54:25

标签: c++ templates boost metaprogramming boost-mpl

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

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();

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

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

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个版本的Command,例如:

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

等等? (甚至不确定这确实会起作用)

还是有另一种更优雅的方法吗? 语法,提到here在那里有用吗? (函数f;)

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

对此有何见解? Regads, D.

4 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:2)

乍一看,可变参数模板似乎是完美的解决方案。不幸的是,它们不适合虚拟功能:

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

这需要executeImpl成为虚拟成员函数模板,但在C ++中没有这样的东西!

答案 2 :(得分: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;
};

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

答案 3 :(得分:0)

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

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等用于其后端实现。它比通过模板元编程以递归方式遍历类型列表容易得多。

至于一般答案,我把它发布在你最初有这个问题的另一个帖子中,另外一个。