min
次の関数の定義
template <typename T, typename U>
constexpr auto
min(T&& t, U&& u) -> decltype(t < u ? t : u)
{
return t < u ? t : u;
}
問題があります:書くことは完全に合法であるようです
min(10, 20) = 0;
これは、Clang 3.5 および g++ 4.9 でテストされています。
解決策は簡単std::forward
です。引数の「右辺値」を復元するために使用するだけです。つまり、本体を変更してdecltype
、
t < u ? std::forward<T>(t) : std::forward<U>(u)
ただし、最初の定義でエラーが発生しない理由を説明するには途方に暮れています。
転送とユニバーサル参照についての私の理解を考えると、整数リテラルが渡されたときと同じように、 と の両方が引数の型t
をu
推測します。int&&
ただし、 の本体内ではmin
、引数に名前があるため、それらは左辺値です。ここで、条件演算子の非常に複雑なルールが登場しますが、適切な行は次のとおりだと思います。
- E2 [および] E3 はどちらも同じ型の glvalue です。この場合、結果の型と値のカテゴリは同じになります。
したがって、戻り値の型も同様である必要がありますoperator?:
。int&&
ただし、(私が知る限り) Clang と g++ の両方がmin(int&&, int&&)
左辺値参照を返すint&
ため、結果に割り当てることができます。
明らかに私の理解にはギャップがありますが、何が欠けているのか正確にはわかりません。ここで何が起こっているのかを正確に説明できる人はいますか?
編集:
Niall が正しく指摘しているように、ここでの問題は条件演算子 (int&&
期待どおりの型の左辺値を返す) ではなく、decltype
. decltype
言うためのルール
式の値カテゴリが左辺値の場合、decltype は T& を指定します
そのため、関数の戻り値は になりますint&& &
。これは、C++ 11 の参照折りたたみ規則により、プレーンになりますint&
(私の予想に反してint&&
)。
しかし、 を使用すると、 (back)std::forward
の 2 番目と 3 番目の引数をoperator?:
右辺値、具体的には xvalue に変換します。xvalues はまだ glvalues であるため (後ろに追いついていますか?)、同じ条件演算子規則が適用され、同じ型と値カテゴリの結果が得られます。つまりint&&
、xvalue です。
ここで、関数が戻ると、別のdecltype
ルールがトリガーされます。
式の値カテゴリが xvalue の場合、decltype は T&& を指定します
今回は、参照の折りたたみによりint&& && = int&&
、さらに重要なことに、関数は xvalue を返します。これにより、私たちが望むように、戻り値に代入することが違法になります。