C++ 如何在C++;?
考虑以下示例:C++ 如何在C++;?,c++,c++11,templates,variadic-templates,template-meta-programming,C++,C++11,Templates,Variadic Templates,Template Meta Programming,考虑以下示例: 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_typ
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?
...
}
};
模板类方法;
模板类方法{
公众:
使用type=Arg;//这不起作用
};
使用参数的模板\u types=typename方法\u traits::type;
模板类节点{
T;
公众:
节点(输入…输入){//如何使其工作?
...
}
};
节点的构造函数的参数取决于方法T::process
的参数。因此,如果类型T
具有签名float进程(float a,int b)
的方法process
,节点的构造函数的签名应如下所示:节点(输入a,输入b)
如何从T::process
提取参数包,以便在节点的构造函数上使用它?使用完美转发():
模板
节点(Args&&…Args){
进程(标准:转发(args)…);
}
显然,您不能以这种方式保存类型列表
using type = Arg;
其中,Arg
是类型的可变列表
但是您可以将它们保存在类型容器中,std::tuple
也可以这样做。因此,我建议修改方法\u 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...>; };
template <typename T, typename ... Args>
struct Node<T, std::tuple<Args...>>
{
T t;
Node (Input<Args> ... inputs)
{ /* do something */ }
};
template <typename, typename>
struct NodeBase;
template <typename T, typename ... Args>
struct NodeBase<T, std::tuple<Args...>>
{
T t;
NodeBase (Input<Args> ...)
{ /* do something */ }
};
现在,您可以使用默认模板值和部分专用化来定义节点
template <typename T, typename TArgs = tTypes<decltype(&T::process)>>
struct Node;
下面是一个完整的工作示例
#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);
}
对于节点
,不需要额外的模板参数,它可以简单地写成
template <typename T>
struct Node
: public NodeBase<T, tTypes<decltype(&T::process)>>
{ using NodeBase<T, tTypes<decltype(&T::process)>>::NodeBase; };
模板
结构体类型
:公共节点库
{使用NodeBase::NodeBase;};
朱利叶斯遵循这个想法,认为(IMHO)甚至更好、更有趣。下面是C++11中基于。这里的区别是:
节点
没有额外的模板参数(而是使用基类和构造函数继承)
- 所需参数的获取方式与问题中显示的略有不同
- 为限定成员函数添加重载很简单
- 引用不是问题(据我所知;请参阅下面的示例打印
42
)
#包括
模板结构类型{};
模板
constexpr类型获取(R(C::*)(Args…)的类型{
返回类型{};
}
模板
constexpr类型获取(R(C::*)(Args…)const的参数类型{
返回类型{};
}
模板
结构NodeImpl;
模板
结构NodeImpl{
NodeImpl(Arg0 v0,Args…){
v0=类型名称std::decay::type(42);
(无效)v0;
}
};
模板
结构体类型
:NodeImpl
{
使用ConstructorArgs=decltype(get_argtypes_of(&T::process));
使用NodeImpl::NodeImpl;
};
结构Foo{
无效进程(int,char,unsigned)常量{}
};
结构条{
无效进程(双重&){}
};
int main(){
节点foo_节点{4,'c',8u};
双参考试验=2.0;
节点栏\节点{reftest};
std::cout这里有一个使用C++14的解决方案(注意:我只在clang中测试过它):
#包括
#包括
结构Foo{
无效进程(int,std::string);
};
模板
结构输入{};
模板
结构提取类型{
使用type=typename Extract\u type::type;
};
模板
结构提取类型{
使用类型=T;
};
模板
typename Extract_type::type Extract(R(T::*)(Args…);
模板
整型常数num参数(R(T::*)(参数…);
模板
结构节点{
模板
节点(输入)使用私有继承和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();
}
您还需要将节点设置为可变模板。@n.m.但是Node
仅依赖于单个类型T
,而不依赖于多个类型types@n.m.当然,使节点的构造函数成为可变模板而不是整个类就足够了?@Arthurtaca它可能是可变模板,这取决于节点存储的内容。请注意,如果mber函数有cv限定符、ref限定符或是C型变量(或在C++17中是noexcept)。请参阅std::is_function
at的丑陋示例定义,以了解如何正确执行该函数。这是一个不错的解决方案,尽管它需要向Node
@睫毛添加第二个模板参数-是;但第二个参数是默认参数;因此,正如您在main()
,您可以将节点
简单地实例化为节点
。这是一个非常好的解决方案。如果默认模板参数不可接受,则可以从具有构造函数继承的基类派生。@Julius-谢谢;我更喜欢默认模板类型的方式,但我可以想象在什么情况下这是一个不可行的解决方案。在在这种情况下,您的想法可能是有用的。另外,您的想法是一个很好的解决方案;如果您希望它与C++11一起使用,它非常简单:避免在get\u argtypes\u of()
中返回auto
,并显式返回类型
。如果您担心类型的重复,您可以简单地返回{};
。(继续)并且,考虑到您只在几个decltypes()
中使用get_argtypes_of()
(因为您只对返回的类型感兴趣,而不对返回的值感兴趣),您也可以声明它而不实现它(如std::declval()
);我的意思是,这就足够了模板constepr auto get_argtypes_of(R(C::*)(Args…);
,未实现该函数。
template <typename T>
struct Node
: public NodeBase<T, tTypes<decltype(&T::process)>>
{ using NodeBase<T, tTypes<decltype(&T::process)>>::NodeBase; };
#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;
}
#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>{ } };
}
#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