2016-12-21 12 views
9

std::applyの可能な実装を検討:パラメータの組を持つ関数(f)を呼び出すときに渡すために、なぜ我々は、実装にタプルstd::get<I>(std::forward<Tuple>(t))...の各要素に対してstd::forwardを実行する必要はありません(tstd :: forwardは明示的なstd :: forwardなしのパラメータをどのように適用しますか?

namespace detail { 
template <class F, class Tuple, std::size_t... I> 
constexpr decltype(auto) apply_impl(F &&f, Tuple &&t, std::index_sequence<I...>) 
{ 
    return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...); 
} 
} // namespace detail 

template <class F, class Tuple> 
constexpr decltype(auto) apply(F &&f, Tuple &&t) 
{ 
    return detail::apply_impl(
     std::forward<F>(f), std::forward<Tuple>(t), 
     std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{}); 
} 

を?

答えて

7

std::getはタプルのrvalue-referenceとlvalue-referenceにオーバーロードされているため、各要素はstd::forwardにする必要はありません。

std::forward<Tuple>(t)はあなたに左辺値(Tuple &)または右辺値(Tuple &&)のいずれかを与えるだろう、とあなたが得るものに応じて、std::getはあなたにT &(左辺値)またはT &&(右辺値)が得られます。 std::getのさまざまなオーバーロードを参照してください。


std::tuplestd::getの詳細のビット -

StoryTellerにより述べたように、タプルのすべてのメンバーは、右辺値から構成されているか、左辺値はここでない妥当であるかどうか、左辺値であります:

double a{0.0}; 
auto t1 = std::make_tuple(int(), a); 
auto t2 = std::make_tuple(int(), double()); 

問題は - タプルは右辺値ですか?はいの場合、そのメンバーを移動することができます。ない場合は、コピーを行う必要がありますが、std::getは対応するカテゴリのメンバーを返すことで既にそれを処理します。

decltype(auto) a1 = std::get<0>(t1); 
decltype(auto) a2 = std::get<0>(std::move(t1)); 

static_assert(std::is_same<decltype(a1), int&>{}, ""); 
static_assert(std::is_same<decltype(a2), int&&>{}, ""); 

戻るstd::forwardと具体例と:第2のケースでながらtupleは、std::tuple<int>&&として転送されるためfの最初の呼び出しで

template <typename Tuple> 
void f(Tuple &&tuple) { // tuple is a forwarding reference 
    decltype(auto) a = std::get<0>(std::forward<Tuple>(tuple)); 
} 

f(std::make_tuple(int())); // Call f<std::tuple<int>>(std::tuple<int>&&); 
std::tuple<int> t1; 
f(t1); // Call f<std::tuple<int>&>(std::tuple<int>&); 

aのタイプはint&&なりtuplestd::tuple<int>&として転送されるため、タイプはint&になります。

+0

nice!つまり、魔法はタプルの側にあります:) –

+0

@ W.F。正確に;) – Holt

+0

'get'' std :: tuple &'を渡し、 'get'の後に、結果の要素を転送するのはどうでしょうか?それは同じように働くでしょうか? –

2

std::forwardは、すべてが正しい値カテゴリのコールサイトに到着することを確認するために使用されます。

しかし、タプルがすべてrvalueのタプルであっても、タプルのすべてのメンバーは左辺値です。

+0

正確には、正価がstd :: invokeの内部で行われても、関数 'f' –

+0

にrvalueの参照を渡したい場合があります。この関数は、パラメータがrvalue参照であるかどうかを知ることができず、いいえ? –

+0

@ W.F。 - 私たちは知ることができます。しかし、Holtのように、タプル内のrvalue参照への一時的な束縛が依然として有効なオブジェクトであると仮定できる唯一の時間は、タプル自体がrvalue参照にバインドされているかどうかです。だから、常に勝つべきタプル自体の価値カテゴリーです。 – StoryTeller

関連する問題