長い間、三項演算子は常に右辺値を返すと思っていました。しかし、驚いたことに、そうではありません。次のコードでは、foo
の戻り値と三項演算子の戻り値の違いがわかりません。
#include <iostream>
int g = 20 ;
int foo()
{
return g ;
}
int main()
{
int i= 2,j =10 ;
foo()=10 ; // not Ok
((i < 3) ? i : j) = 7; //Ok
std::cout << i <<","<<j << "," <<g << std::endl ;
}
i
とj
はどちらもglvaluesです(詳細は この値カテゴリのリファレンス を参照) 。
次に、 この条件演算子の参照 を読むと、次のようになります。
4)E2とE3が同じ型と同じ値のカテゴリのglvalueである場合、結果は同じ型と値のカテゴリを持ちます
したがって、(i < 3) ? i : j
の結果は、割り当て可能なglvalueになります。
しかし、そのようなことをすることは、私がお勧めすることではありません。
このルールの詳細は [expr.cond] にあります。タイプと値カテゴリのいくつかの組み合わせには、多くのブランチがあります。しかし、最終的には、式はデフォルトの場合のprvalueです。あなたの例のケースはパラグラフ5でカバーされています:
2番目と3番目のオペランドが同じ値カテゴリのglvalueであり、同じ型である場合、結果はその型と値カテゴリになり、2番目または3番目のオペランドがビットフィールドである場合、またはどちらもビットフィールドです。
変数名であるi
とj
は、どちらもint
型の左辺値式です。したがって、条件演算子はint
lvalueを生成します。
三項条件演算子は、2番目と3番目のオペランドの型が左辺値である場合、左辺値を生成します。
関数テンプレートis_lvalue
(以下)は、オペランドが左辺値かどうかを調べ、それを関数テンプレートisTernaryAssignable
で使用して、代入できるかどうかを調べます。
最小限の例:
#include <iostream>
#include <type_traits>
template <typename T>
constexpr bool is_lvalue(T&&) {
return std::is_lvalue_reference<T>{};
}
template <typename T, typename U>
bool isTernaryAssignable(T&& t, U&& u)
{
return is_lvalue(std::forward<T>(t)) && is_lvalue(std::forward<U>(u));
}
int main(){
int i= 2,j =10 ;
((i < 3) ? i : j) = 7; //Ok
std::cout << std::boolalpha << isTernaryAssignable(i, j); std::cout << '\n';
std::cout << std::boolalpha << isTernaryAssignable(i, 10); std::cout << '\n';
std::cout << std::boolalpha << isTernaryAssignable(2, j); std::cout << '\n';
std::cout << std::boolalpha << isTernaryAssignable(2, 10); std::cout << '\n';
}
出力:
true
false
false
false
注:isTernaryAssignable
に渡すオペランドは、減衰を受けないようにする必要があります(たとえば、ポインターに減衰する配列) )。