C ++でパラメータパックを渡す方法は?

次の例を見てください。

template  class method_traits;
template  class method_traits {
public:
    using type = Arg;//this does not work
};

template  using argument_types = typename method_traits::type;

template  class Node {
    T t;
public:
    Node(Input>... inputs) {//how do I make this work?
        ...
    }
};

The arguments of the constructor of Node depend on the arguments of the method T::process. So if a type T has a method process of the signature float process(float a, int b) the signature of the constructor of Node should look like this: Node(Input a, Input b).

Node のコンストラクタで使用するために T :: process からパラメータパックを抽出する方法

10
入力とはそれは質問にとって重要ですか?
追加された 著者 Barry,
メンバー関数にcv-qualifiers、ref-qualifiers、またはCスタイルの可変長(またはC ++ 17ではnoexcept)がある場合、ほとんどの答えは失敗します。 jaにある std :: is_function の見苦しいサンプル定義を参照してください。それが適切に行われる必要がある方法についてのヒントについてはcppreference.com/w/cpp/types/is_function を参照してください。
追加された 著者 aschepler,
Nodeも可変個のテンプレートにする必要があります。
追加された 著者 n.m.,
@ArthurTaccaは、Nodeが保存しているものによって異なります。
追加された 著者 n.m.,
「私のメンバ関数はT :: somefunctionと同じシグネチャを持つことになるでしょう。それはC ++構文の欠点です。それを克服するには、実際の依存関係を表現しない人工パラメータパックを導入する必要があります。
追加された 著者 n.m.,
@ n.m。しかし Node は単一の型 T にのみ依存し、複数の型には依存しません
追加された 著者 eyelash,
私の場合、 NodeInput をタプルに格納するので、コンストラクターを可変テンプレートにするとよいでしょう。
追加された 著者 eyelash,
@バリー質問には重要ではありません。引数がまったく同じではなく、互いに依存していることを示すためのものです。
追加された 著者 eyelash,
@ n.m。もちろん、クラス全体ではなく、Nodeのコンストラクタを可変テンプレートにするだけで十分ですか。
追加された 著者 Arthur Tacca,

5 答え

明らかにあなたはこのようにタイプのリストを保存することができません

    using type = Arg;

ここで、 Arg はさまざまな型のリストです。

しかし、あなたはそれらを型コンテナに保存することができ、 std :: tuple でもこれを行うことができます。そのため、 method_traits の特殊化を次のように変更することをお勧めします。

template 
struct method_traits;

template 
struct method_traits
 { using tTypes = std::tuple; };

argument_types を書き換えて std :: tuple をインターセプトします。

template 
using tTypes = typename method_traits::tTypes;

これで、デフォルトのテンプレート値と部分特殊化トリック定義ノードを使用できます

template >
struct Node;

In this way, instantiating a Node object, you effectively get a Node that is a Node> with the wanted Args....

ですから、以下のように Node の以下の部分的な特殊化を定義するだけです。

template 
struct Node>
 {
   T t;

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

以下は完全に機能する例です。

#include 
#include 

template 
struct tWrapper
 { using type = T; };

template 
using Input = typename tWrapper::type;

template 
struct method_traits;

template 
struct method_traits
 { using tTypes = std::tuple; };

template 
using tTypes = typename method_traits::tTypes;

template >
struct Node;

template 
struct Node>
 {
   T t;

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

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

int main ()
 {
   Node nf(1.0f, 2);
 }

- 編集 -

Julius(およびOP自身)が指摘したように、この解決策はデフォルトのテンプレート値を持つ追加のテンプレートタイプを必要とします。

この単純化したケースでは問題はありませんが、この追加のテンプレート引数を追加できない状況を想像できます(例: Node がテンプレート引数の可変リストを受け取る場合)。

そのような場合、Juliusは Node の追加のテンプレートパラメータを避けて、 TArgs を受け取るテンプレートベースクラスを追加することで、解決策を少し複雑にする方法を提案します引数、およびコンストラクタの継承で機能します。

つまり、次のように NodeBase を定義します。

template 
struct NodeBase;

template 
struct NodeBase>
 {
   T t;

   NodeBase (Input ...)
    { /* do something */ }
 };

Node 用の追加のテンプレートパラメータは必要ありません。

template 
struct Node
   : public NodeBase>
 { using NodeBase>::NodeBase; };

Juliusは、このアイデアに従って、解決策を用意しました。

6
追加された
2番目のテンプレートパラメータを Node に追加する必要がある場合でも、優れた解決策
追加された 著者 eyelash,
@eyelash - はい。しかし、2番目のパラメータはデフォルトのものです。そのため、 main()に表示されるように、 Node を Node としてインスタンス化できます。
追加された 著者 max66,
@ Julius - ありがとうございます。私はデフォルトのテンプレートタイプのやり方を好むが、それが現実的でない解決策である状況を想像することができる。そのような状況では、あなたの考えは役に立つかもしれません。
追加された 著者 max66,
とてもいい解決策です。そのデフォルトのテンプレートパラメータが受け入れられない場合は、コンストラクタを継承した基本クラスから派生させることができます。
追加された 著者 Julius,

max66のすばらしい回答に基づいたC ++ 11(max66のコメントによる)の一例です。違いはここにあります:

  • no additional template argument for Node (instead, using a base class and constructor inheritance)
  • the desired arguments are obtained with a slightly different style than shown in the question
    • adding overloads for qualified member functions is simple
    • references are not a problem (as far as I can tell; see example below printing 42)

http://coliru.stacked-crooked.com/a/53c23e1e9774490c

#include 

template struct Types {};

template
constexpr Types get_argtypes_of(R (C::*)(Args...)) {
    return Types{};
}

template
constexpr Types get_argtypes_of(R (C::*)(Args...) const) {
    return Types{};
}

template
struct NodeImpl;

template
struct NodeImpl> {
  NodeImpl(Arg0 v0, Args...) {
    v0 = typename std::decay::type(42);
    (void)v0;
  }
};

template
struct Node
  : NodeImpl
{
  using ConstructorArgs = decltype(get_argtypes_of(&T::process));
  using NodeImpl::NodeImpl;
};

struct Foo {
    void process(int, char, unsigned) const {}
};

struct Bar {
    void process(double&) {}
};

int main() {
    Node foo_node{4, 'c', 8u};

    double reftest = 2.0;
    Node bar_node{reftest};
    std::cout << reftest << std::endl;
}
2
追加された
また、あなたのものは素晴らしい解決策です。 C ++ 11で動作させたい場合は、とても簡単です。 get_argtypes_of()auto を返さず、 Types Types の繰り返しが心配な場合は、単に return {}; を入力してください。 (持続する)
追加された 著者 max66,
また、 get_argtypes_of()を2つの decltypes()でのみ使用することを考慮すると(戻り値の型にのみ関心があり、戻り値には関心がないため)、宣言して実装しない( std :: declval()として)。 テンプレート<�クラスR、クラスC、クラス... Args> constexpr auto get_argtypes_of(R(C :: *)(Args ...)); で十分です。関数。
追加された 著者 max66,

完全転送を使用する(ライブ

template
Node(Args&&... args) {
    process(std::forward(args)...);
}
1
追加された

プライベート継承とCRTPを使うのはどうですか。

#include 
#include 

template  struct method_traits;

template 
struct method_traits {
public:
    using parameter_pack = std::tuple;
};

template  struct Base;

template 
struct Base> {
    void execute_constructor(Ts&&... ts) { 
        Derived* d = static_cast(this);
        d->t.process(std::forward(ts)...);
        d->num = sizeof...(Ts);
    }
    virtual ~Base() = default;
};

template 
class Node : Base, typename method_traits::parameter_pack> {
    T t;
    int num;
public:
    using Base = Base, typename method_traits::parameter_pack>;
    friend Base; //So that Base can do whatever it needs to Node's data members.
    template 
    Node (Ts&&... ts) {
        Base::execute_constructor(std::forward(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 n(5, 'a', true);
    std::cin.get();
}

出力:

foo(5, a, true) carried out.
num = 3
0
追加された

これはC ++ 14を使った解決策です。 (注:私はclangでそれをテストしただけです)

#include 
#include 

struct Foo {
    void process(int, std::string);
};

template 
struct Input { };

template 
struct Extract_type {
    using type = typename Extract_type::type;
};

template 
struct Extract_type<0, T, Types...> {
    using type = T;
};

template 
typename Extract_type::type extract(R (T::*)(Args...));

template 
std::integral_constant num_args(R (T::*)(Args...));

template 
struct Node {
    template 
    Node(Input&&... args) 
    : Node(std::make_index_sequence(&T::process))::value>{ }, std::forward<input>(args)...)
    {}

    template 
    Node(std::index_sequence, Input(&T::process))>...) {}
};


int main() {
    Node b{ Input{ }, Input{ } };
}

http://coliru.stacked-crooked.com/a/da7670f80a229931

0
追加された