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