请考虑这个 - 可能写得不好 - 例如:
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.
答案 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等用于其后端实现。它比通过模板元编程以递归方式遍历类型列表容易得多。
至于一般答案,我把它发布在你最初有这个问题的另一个帖子中,另外一个。