如何在C++中传递参数包?

eye*_*ash 10 c++ templates template-meta-programming variadic-templates c++11

请考虑以下示例:

template <class T> class method_traits;
template <class T, class Ret, class... Arg> class method_traits<Ret(T::*)(Arg...)> {
public:
    using type = Arg; // this does not work
};

template <class T> using argument_types = typename method_traits<T>::type;

template <class T> class Node {
    T t;
public:
    Node(Input<argument_types<decltype(&T::process)>>... inputs) { // how do I make this work?
        ...
    }
};
Run Code Online (Sandbox Code Playgroud)

构造函数Node<T>的参数取决于方法的参数T::process.因此,如果类型T具有process签名方法,float process(float a, int b)则构造函数的签名Node<T>应如下所示:Node(Input<float> a, Input<int> b).

如何从T::process构造函数中提取参数包以使用它Node

max*_*x66 6

显然,您无法以这种方式保存类型列表

    using type = Arg;
Run Code Online (Sandbox Code Playgroud)

哪里Arg是可变的类型列表.

但是你可以将它们保存在一个类型的容器中,std::tuple也可以做到这一点.所以我建议修改method_traits专业化如下

template <typename T>
struct method_traits;

template <typename T, typename Ret, typename... Args>
struct method_traits<Ret(T::*)(Args...)>
 { using tTypes = std::tuple<Args...>; };
Run Code Online (Sandbox Code Playgroud)

并重写argument_types以拦截std::tuple

template <typename T>
using tTypes = typename method_traits<T>::tTypes;
Run Code Online (Sandbox Code Playgroud)

现在您可以使用默认模板值和部分特化技巧定义节点

template <typename T, typename TArgs = tTypes<decltype(&T::process)>>
struct Node;
Run Code Online (Sandbox Code Playgroud)

通过这种方式,实例化一个Node<T>对象,你可以有效地得到Node<T, tTypes<decltype(&T::process)>一个Node<T, std::tuple<Args...>>与想要的对象Args....

因此,您可以简单地定义以下部分特化Node,如下所示

template <typename T, typename ... Args>
struct Node<T, std::tuple<Args...>>
 {
   T t;

   Node (Input<Args> ... inputs)
    { /* do something */ }
 };
Run Code Online (Sandbox Code Playgroud)

以下是一个完整的工作示例

#include <tuple>
#include <type_traits>

template <typename T>
struct tWrapper
 { using type = T; };

template <typename T>
using Input = typename tWrapper<T>::type;

template <typename T>
struct method_traits;

template <typename T, typename Ret, typename... Args>
struct method_traits<Ret(T::*)(Args...)>
 { using tTypes = std::tuple<Args...>; };

template <typename T>
using tTypes = typename method_traits<T>::tTypes;

template <typename T, typename TArgs = tTypes<decltype(&T::process)>>
struct Node;

template <typename T, typename ... Args>
struct Node<T, std::tuple<Args...>>
 {
   T t;

   Node (Input<Args> ... inputs)
    { /* do something */ }
 };

struct foo
 {
   float process (float a, int b)
    { return a+b; }
 };

int main ()
 {
   Node<foo> nf(1.0f, 2);
 }
Run Code Online (Sandbox Code Playgroud)

- 编辑 -

正如Julius(以及OP本身)所指出的,此解决方案需要一个具有默认模板值的附加模板类型.

在这个简化的情况下不是问题,但我可以想象无法添加此附加模板参数的情况(例如:如果Node接收模板参数的可变参数列表).

在这些情况下,Julius提出了一种使解决方案复杂化的方法,但允许避免额外的模板参数Node:添加模板基类,接收TArgs参数,以及使用构造函数继承.

即:定义NodeBase如下

template <typename, typename>
struct NodeBase;

template <typename T, typename ... Args>
struct NodeBase<T, std::tuple<Args...>>
 {
   T t;

   NodeBase (Input<Args> ...)
    { /* do something */ }
 };
Run Code Online (Sandbox Code Playgroud)

不需要额外的模板参数,因为Node它可以简单地写成

template <typename T>
struct Node
   : public NodeBase<T, tTypes<decltype(&T::process)>>
 { using NodeBase<T, tTypes<decltype(&T::process)>>::NodeBase; };
Run Code Online (Sandbox Code Playgroud)

朱利叶斯遵循这个想法,准备了一个解决方案,(恕我直言)更好,更有趣.