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
?
显然,您无法以这种方式保存类型列表
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)
朱利叶斯遵循这个想法,准备了一个解决方案,(恕我直言)更好,更有趣.