如何在C++中传递参数包
How to pass around parameter packs in C++?
考虑以下示例:
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?
...
}
};
Node<T>
的构造函数的参数取决于方法T::process
的参数。因此,如果类型T
具有签名float process(float a, int b)
的方法process
,则Node<T>
的构造函数的签名应该如下所示:Node(Input<float> a, Input<int> b)
。
如何从T::process
中提取参数包以将其用于Node
的构造函数?
显然,您不能以这种方式保存类型列表
using type = Arg;
其中CCD_ 10是类型的可变列表。
但是您可以将它们保存在类型容器中,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...>; };
并重写CCD_ 13以拦截CCD_
template <typename T>
using tTypes = typename method_traits<T>::tTypes;
现在您可以使用默认模板值和部分专业化技巧定义节点
template <typename T, typename TArgs = tTypes<decltype(&T::process)>>
struct Node;
通过这种方式,实例化一个Node<T>
对象,可以有效地获得一个Node<T, tTypes<decltype(&T::process)>
,它是一个具有所需Args...
的Node<T, std::tuple<Args...>>
。
因此,您可以简单地将Node
的以下部分专业化定义为以下
template <typename T, typename ... Args>
struct Node<T, std::tuple<Args...>>
{
T t;
Node (Input<Args> ... inputs)
{ /* do something */ }
};
以下是的完整工作示例
#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);
}
--编辑--
正如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 */ }
};
对于Node
,不需要额外的模板参数,它可以简单地写为
template <typename T>
struct Node
: public NodeBase<T, tTypes<decltype(&T::process)>>
{ using NodeBase<T, tTypes<decltype(&T::process)>>::NodeBase; };
Julius遵循这个想法,准备了一个更好、更有趣的解决方案(IMHO)。
使用完美转发(实时):
template<typename... Args>
Node(Args&&... args) {
process(std::forward<Args>(args)...);
}
这里是C++11中的一个例子(感谢max66的注释),它基于max66的出色答案。这里的区别:
Node
没有额外的模板参数(而是使用基类和构造函数继承)- 所需的论点是用与问题中所示略有不同的风格获得的
- 为合格的成员函数添加重载很简单
- 引用不是问题(据我所知,请参阅下面打印
42
的示例)
http://coliru.stacked-crooked.com/a/53c23e1e9774490c
#include <iostream>
template<class... Ts> struct Types {};
template<class R, class C, class... Args>
constexpr Types<Args...> get_argtypes_of(R (C::*)(Args...)) {
return Types<Args...>{};
}
template<class R, class C, class... Args>
constexpr Types<Args...> get_argtypes_of(R (C::*)(Args...) const) {
return Types<Args...>{};
}
template<class T, class ConstructorArgs>
struct NodeImpl;
template<class T, class Arg0, class... Args>
struct NodeImpl<T, Types<Arg0, Args...>> {
NodeImpl(Arg0 v0, Args...) {
v0 = typename std::decay<Arg0>::type(42);
(void)v0;
}
};
template<class T>
struct Node
: NodeImpl<T, decltype(get_argtypes_of(&T::process))>
{
using ConstructorArgs = decltype(get_argtypes_of(&T::process));
using NodeImpl<T, ConstructorArgs>::NodeImpl;
};
struct Foo {
void process(int, char, unsigned) const {}
};
struct Bar {
void process(double&) {}
};
int main() {
Node<Foo> foo_node{4, 'c', 8u};
double reftest = 2.0;
Node<Bar> bar_node{reftest};
std::cout << reftest << std::endl;
}
这里有一个使用C++14的解决方案。(注意:我只是在叮当声中测试过它):
#include <string>
#include <utility>
struct Foo {
void process(int, std::string);
};
template <typename T>
struct Input { };
template <std::size_t N, typename T, typename ...Types>
struct Extract_type {
using type = typename Extract_type<N - 1, Types...>::type;
};
template <typename T, typename ...Types>
struct Extract_type<0, T, Types...> {
using type = T;
};
template <typename T, std::size_t N, typename R, typename ...Args>
typename Extract_type<N, Args...>::type extract(R (T::*)(Args...));
template <typename T, typename R, typename ...Args>
std::integral_constant<std::size_t, sizeof...(Args)> num_args(R (T::*)(Args...));
template <typename T>
struct Node {
template <typename ...Args>
Node(Input<Args>&&... args)
: Node(std::make_index_sequence<decltype(num_args<T>(&T::process))::value>{ }, std::forward<Input<Args>>(args)...)
{}
template <std::size_t ...Indices>
Node(std::index_sequence<Indices...>, Input<decltype(extract<T, Indices>(&T::process))>...) {}
};
int main() {
Node<Foo> b{ Input<int>{ }, Input<std::string>{ } };
}
http://coliru.stacked-crooked.com/a/da7670f80a229931
使用私有继承和CRTP如何?
#include <tuple>
#include <iostream>
template <typename Method> struct method_traits;
template <typename T, typename Ret, typename... Args>
struct method_traits<Ret(T::*)(Args...)> {
public:
using parameter_pack = std::tuple<Args...>;
};
template <typename Derived, typename Tuple> struct Base;
template <typename Derived, typename... Ts>
struct Base<Derived, std::tuple<Ts...>> {
void execute_constructor(Ts&&... ts) {
Derived* d = static_cast<Derived*>(this);
d->t.process(std::forward<Ts>(ts)...);
d->num = sizeof...(Ts);
}
virtual ~Base() = default;
};
template <typename T, typename... Rest>
class Node : Base<Node<T, Rest...>, typename method_traits<decltype(&T::process)>::parameter_pack> {
T t;
int num;
public:
using Base = Base<Node<T, Rest...>, typename method_traits<decltype(&T::process)>::parameter_pack>;
friend Base; // So that Base can do whatever it needs to Node<T, Rest...>'s data members.
template <typename... Ts>
Node (Ts&&... ts) {
Base::execute_constructor(std::forward<Ts>(ts)...);
std::cout << "num = " << num << 'n';
}
};
struct foo {
void process(int a, char c, bool b) {
std::cout << "foo(" << a << ", " << c << ", " << std::boolalpha << b << ") carried out.n";
}
};
int main() {
Node<foo> n(5, 'a', true);
std::cin.get();
}
输出:
foo(5, a, true) carried out.
num = 3
- 如何反转整数参数包
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 如何使用默认参数等选择模板专业化
- 模板参数替换失败,并且未完成隐式转换
- 具有默认模板参数的多态类的模板推导失败
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 函数调用中参数的顺序重要吗
- 部分定义/别名模板模板参数
- 模板-模板参数推导:三个不同的编译器三种不同的行为
- 使用不带参数的函数访问结构元素
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 如何在OMNET++中指定与命令行参数组合的输出文件名
- 如何使用Luacneneneba API正确读取字符串和表参数
- 在派生函数中指定void*参数
- 视图中的参数推导失败:take_while
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- 使用指向成员的指针将成员函数作为参数传递
- 没有名称的C++模板参数
- 如何将enable-if与模板参数和参数包一起使用