2016-05-29 11 views
9

は、私はこの機能を持っていると言いますか?私は心配しています。move()またはforward()は通常、a...がlvaluesなので、明示的に行う必要があります。それらを転送/移動するためにタプル仲介が必要ですか?もしそうなら、インデックストリックを使わずにタプルをパラメータパックに解凍する簡単な方法がありますか?転送または移動によってパラメータパックを取得する方法はありますか?</p> <pre><code>template <typename ...A> void test(A&& ...a) { [=]() { }; } </code></pre> <p>はラムダに転送するか、単に値によってコピーされようとしてパラメータパックです:

+0

参考にしてキャプチャしてから、ラムダボディの前方を使用するのはどうですか?価値観を捉えることはあなたのためには行いません。 – Arunmu

+0

また、あなたがしたいことに擬似コードを書くことができれば助けになります。 – Arunmu

答えて

6

std::bindで実行できる残りの有用な機能の1つです。キャプチャがbindによって実行され、キャプチャされる値は、キャプチャレスジェネリックラムダに引数として渡されます。このGCCはとの問題を持っていると思わ

template <typename... A> 
auto test(A&&... a) 
{ 
    auto f = [](auto&&... a) 
    { 
     // use a... 
    }; 
    return std::bind(f, std::forward<A>(a)...); 
} 

Live demo

クランと上記の作品が、偽volatile修飾子。

template <typename... A> 
auto test(A&&... a) 
{ 
    auto f = [](auto&&... a) 
    { 
     // use a... 
    }; 
    return [f, tup = std::make_tuple(std::forward<A>(a)...)]() mutable { std::apply(f, tup); }; 
} 

Live demo

私たちは、最初のラムダのパラメータリストにタプルをアンパックするstd::apply(C++ 17)を呼び出し、二ラムダでtupleをキャプチャすることによりbindせずにそれを行うことができますClangとGCCで動作します。 applyは、あなたが避けたいインデックスのトリックで実装されていますが、あなたはそれにさらされていません。 mutableは、2番目のラムダの呼び出し演算子が非constなので、タプル要素はconstの資格を得ることになりません。

7

1つの方法は、ハスケルの意味でファンクタを書くことです。まあ、非常にばかではないvariaryicものです。

署名(Ts...)->(((Ts...)->X) -> X)の機能を記述します。つまり、パックを取り、関数を返す関数です。返された関数は、そのパックを取り出して評価する関数を取ることができます。

template<class...Ts> 
auto make_functor(Ts&&...ts); // TODO 

これが済むと、簡単に問題を解決できます。

template<class ...A> 
auto test(A&& ...a) { 
    return [unpack_a=make_functor(std::forward<A>(a)...)]() mutable 
    { 
    return unpack_a([&](auto&&...a){ 
     // here you have access to a... 
     return sizeof...(a); 
    }); 
    }; 
} 

testパックを取り、(まあ、パックと何もしない)、そのパックのサイズを返す関数を返します。

make_functorは簡単ではありません。基本的には、手動でラムダを書いて、タプルにargsを格納し、operator()で索引をトリックします。

実際には、のマニュアル擬似ラムダクラスに格納して解凍し、後で再利用します。

2番目の考えでは、タプルをとり、保存してから遅れてstd::applyを使用する遅延適用を書くほうがよい場合があります。

template<class...Ts> 
auto delayed_apply(std::tuple<Ts...> tup){ 
    return [tup=std::move(tup)](auto&&f)->decltype(auto) mutable{ 
    return std::experimental::apply(decltype(f)(f), std::move(tup)); 
    }; 
} 

これにより、パラメータの値/精度が失われることはありません。

template<class ...A> 
auto test(A&& ...a) { 
    return [unpack_a=delayed_apply(std::forward_as_tuple(std::forward<A>(a)...))]() mutable 
    { 
    return unpack_a([&](auto&&...a){ 
     // here you have access to a... 
     return sizeof...(a); 
    }); 
    }; 
} 

これには、std::experimental::applyが必要です。

あなたは右辺値にしたいと参照として左辺値のままにした場合:

unpack_a=delayed_apply(std::tuple<A...>(std::forward<A>(a)...)) 

を使用すると、両方のLとRの値を格納する場合:

unpack_a=delayed_apply(std::make_tuple(std::forward<A>(a)...)) 

この方法、あなたが見ることができるように多くの制御を与える。

std::experimental::applyが必要な場合は、リファレンス実装があります。スマートフォンに書き込むものより優れています。

make_functordelayed_applyで書くことができますが、逆は真ではありません。

混乱している場合は、unpack_aがラムダを取り、unpack_aを作成するために使用されたタプルをアンパックします。基本的には、パック全体である1つのオブジェクトを格納し、ラムダ本体の内部に必要なときに展開します。

アンパックが「複数回」sometimssと「1回だけ」他の時間動作するようにするには、constとnon-const、さらにはrvalueオーバーロードの両方を処理するより長いdelayed_applyが必要な場合があります。ラムダではなく、クラスを返す必要があります。煩わしい。私が思うに、コンパイルしていないコード例を作成しました。

フォーティナルにこの種のものは、一度だけ書いてあります。

+0

これは内部転送が必要ですか? 'std :: forward_as_tuple(std :: forward (a)...)' – coyotte508

+0

@coyはユースケースによって異なります。場合によっては時にはそうではありません。 – Yakk

2

なぜ値渡しですか?転送はトップファンクションにのみ行われます。

はのは、あなたの関数は、そこから

void test(int,string&,float&&) 
{ 
    [=]() 
    { 
    }; 
} 

のように匿名ラムダ値によって意志コピーintstring&float&&を見ていきますので、あなたは、例えばintstd::string&float&&を渡しましょう。参照のコピーはまだコピーです。 tupleを使用して引数を再度パックし、ラムダの中にそれらを解凍することができます。

ラムダの内部でタプルを使用する方法は?

  1. 我々は通常、可変長引数テンプレートと
  2. を行うようにまず完璧にタプルで引数を取り込む
+0

はい、しかし、 'get <>()'へのインデックスなしで解凍する方法 – user1095108

+0

参照のコピーは、このコンテキストの値であり、もはや参照ではありません。そして、このコピーはコピーによって生成され、rvaluesからの移動ではありません。 – Yakk

+0

@Yakkあなたは正しいです、私はそれを削除しました –

2

std::applyのいくつかの非標準の実装を見つけてanotehr機能の引数としてタプルを使用する再帰を使用します転送:あなたの後の関数呼び出しではタプルを解凍しhttps://stackoverflow.com/a/7858971/835629

template <typename ...A> 
void test(A&& ...a) 
{ 
    [tup= std::tuple<A...>(std::forward<A>(a)...)]() 
    { 
     //tup should contain the forwarded elements 
    }; 
} 

は次にこの回答を使用しています。

//utils 
template<int ...> 
struct seq { }; 

template<int N, int ...S> 
struct gens : gens<N-1, N-1, S...> { }; 

template<int ...S> 
struct gens<0, S...> { 
    typedef seq<S...> type; 
}; 


template<typename F, typename T, int ...S> 
void unpackTupleToFunction_utils(F func, const T &tup, seq<S...>) { 
func(std::get<S>(tup) ...); 
} 

template<typename F, typename ...Args, int ...S> 
void unpackTupleToFunction(F func, const std::tuple<Args...> &tup) { 
unpackTupleToFunction_utils(func, tup, typename gens<sizeof...(Args)>::type()); 
} 

そして最終的にそれで関数を呼び出すために、ラムダの内側にタプルを解凍するには:

template <typename ...Args> 
void test(Args&& ...a) { 
    auto lambda = [tup= std::tuple<Args...>(std::forward<Args>(a)...)]() 
    { 
    unpackTupleToFunction(f, tup); 
    }; 

    lambda(); 
    lambda(); 
    lambda(); 
} 

PS:それは[a = (std::forward<Args>(a)...)](){};のようなものがコンパイルされないのは残念です。

+0

はい、はい、しかしインデックストリックは私が避けようとしているものですが、あなたは完全に正しいです。 – user1095108

関連する問題

 関連する問題