std::tuple转换为成员函数

3

我仍在努力掌握元编程,但却遇到了难题。

我想做的是创建一个类/结构体/其他,给它提供一个 std::tuple,并根据元组中的对象类型自动生成成员函数。目标是让类派生自 MessageHandler

例如:

typedef std::tuple< MessageA, MessageB, MessageC > MessageSet;

template< class T >
class MessageHandler
{
  // some magic metaprogramming would "create"...
  virtual void processMsg( const MessageA& ) = 0;
  virtual void processMsg( const MessageB& ) = 0;
  virtual void processMsg( const MessageC& ) = 0;
};

我看到过一些关于模板中不能使用虚函数的说法,但不确定在C++11中是否仍然如此。

谢谢。


2
你可以在模板中拥有虚函数。但是你不能拥有"虚成员函数模板"。 - pmr
1
转念一想:了解你实际想要实现什么很有趣。像我的或者nawaz的解决方案可以做出你展示的效果,但可能不会有你想要的那种多态性。 - pmr
2个回答

5
答案是变量模板、部分特化和继承:
//primary template!
template<typename T>
class MessageHandler;

//variadic template, partial specialization and inheritance!
template<typename H, typename ...T>
class MessageHandler<std::tuple<H,T...>>  : public MessageHandler<std::tuple<T...>>
{
    virtual void processMsg( const H& ) = 0;
};

template<typename T>
class MessageHandler<std::tuple<T>>
{
    virtual void processMsg( const T& ) = 0;
};

4
您不需要使用元组来完成这个任务:
struct MessageA{};struct MessageB{};struct MessageC{};

template <typename T>
struct message_interface {
  virtual void processMessage(const T& t) = 0;
};

template< typename... Args >
struct message_handler : public message_interface<Args>...
{};

struct message_impl : message_handler<MessageA, MessageB, MessageC>
{
  void processMessage(const MessageA&){}
  void processMessage(const MessageB&){}
  void processMessage(const MessageC&){}
};

int main()
{
  message_impl i;
  return 0;
}

建议检查参数列表是否唯一,并进行静态断言。此外,请确保它不包含引用类型或其他不必要的内容。当您尝试形成参数类型时,这些通常会导致错误,但这将为您的用户节省一些麻烦。

编辑:如果您绝对需要支持tuple,请添加一个专门的实现:

template< typename... Args >
struct message_handler< std::tuple<Args...> > : public message_interface<Args>...
{};

2
不必为 std::tuple<Args...> 提供专门化,可以编写一个实用程序,使得 bind<message_impl, std::tuple<Args...>>::type 等于 message_impl<Args...>。在操作类型列表时非常有用。 - Luc Danton
@LucDanton 是的,从元组中提取参数列表是必不可少的。一般来说,我避免使用可变参数模板进行元编程,而是专门使用MPL。这样可以避免很多问题。 - pmr
太好了,谢谢!你有什么建议可以用来处理可变模板?整个周末我一直在搜寻互联网,但我从不知道你可以在基类定义之后添加省略号(如消息处理程序)。 - RMHarris157
然而,仍有一件事让我困惑...考虑到上述所有内容,为什么我不能在message_impl的主体中引入template< class T > void processMsg( const T& ) { }?如果编译器足够了解创建虚拟签名,那么它为什么不足够了解创建存根函数呢?这将允许我创建一个“默认”的对象处理程序,并仅覆盖我关心的消息。谢谢! - RMHarris157
没事了,我懂了。如果我把 message_interface 从 = 0 改成 { },那么我就可以有选择地专门化 message_impl - RMHarris157
@user1561041,我有点困惑你在问什么。特别是我不明白你所说的“有选择性地专门化”的意思。至于变长模板,我有一份教程清单,但不知道如何将它们上传到SO上。 - pmr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接