C++関数で三目演算子のような機能を実現する方法

10592 ワード

問題の引き出し
今日aikilisは私に2つの質問をしました:1次のコードは合法ですか?
( i > 0 ? i : j ) = 1;

2 quest(bool,type,type)としてプロトタイプされた関数で三文字の機能を実現するにはどうすればいいですか.
実験を経て、最初の問題の答えは肯定的で、もとはこのように使ったことがありませんが.2つ目の問題は確かに頭を悩ませた.
トリプルシンボルの性質
void test0() {
        int i = 0, j = 0, k;
        ( i > 0 ? i : j ) = 3;  // ok
        ( i > 0 ? i : 2 ) = 3;  // error
        ( i > 0 ? 1 : j ) = 3;  // error
        ( i > 0 ? 1 : 2 ) = 3;  // error
        k = ( j > 0 ? i : j );  // ok
        k = ( j > 0 ? i : 2 );  // ok
        k = ( i > 0 ? 1 : j );  // ok
        k = ( i > 0 ? 1 : 2 );  // ok
}

すなわち,右側に値を付与する場合,三目文字は左値右値を返してもよい.ただし、付与の左側に置くと、三目文字の戻り値は左でなければなりません.
私たちの目標は次のコードを実現することです.
void test1() {
        int i = 0, j = 0, k;
        quest( i > 0, i, j ) = 3;  // ok
        quest( i > 0, i, 2 ) = 3;  // error
        quest( i > 0, 1, j ) = 3;  // error
        quest( i > 0, 1, 2 ) = 3;  // error
        k = quest( j > 0, i, j );  // ok
        k = quest( j > 0, i, 2 );  // ok
        k = quest( i > 0, 1, j );  // ok
        k = quest( i > 0, 1, 2 );  // ok
}

失敗した試行
1最初のアイデアは、関数のリロードによって、左の値を入力すると左の値の参照を返し、右の値を入力すると右の値を返します.
//       
template< typename T >
T&  quest( bool cond, T& true_val, T& false_val ) {
    if( cond ) return true_val;
    return false_val;
}
//       
template< typename T >
T  quest( bool cond, const T true_val, const T false_val ) {
    if( cond ) return true_val;
    return false_val;
}

しかし、これはだめです.コンパイル結果:
void test1() {
        int i = 0, j = 0, k;
        quest( i > 0, i, j ) = 3;  // ambiguous,           
        quest( i > 0, i, 2 ) = 3;  // error
        quest( i > 0, 1, j ) = 3;  // error
        quest( i > 0, 1, 2 ) = 3;  // error
        k = quest( j > 0, i, j );  // ambiguous,           
        k = quest( j > 0, i, 2 );  // ok
        k = quest( i > 0, 1, j );  // ok
        k = quest( i > 0, 1, 2 );  // ok
}

標準的な答えと2つの不一致が見られます.true_valとfalse_valが左の値の場合、コンパイラはどのバージョンを呼び出すかを区別できません.
2それから私は右の値の引用を思い出して、調べてみると、右の値の引用は本当にいくつかの比較的に面白い特性があります:a>右の値の引用は関数のパラメータとして、右の値あるいは臨時の変数しか伝達できなくて、左の値あるいは左の値の引用b>を伝達できませんテンプレートあるいはtypedefの中で引用の折り畳みを使うことができるならば、折りたたみルールは次のとおりです.-右参照の右参照を右参照に折りたたむ(T&&&&&&&T&)-他の場合は左参照とみなされます(T&&&T&)c>左値が渡された場合、Tがプロトタイプであると推定された場合、T&&右値参照が構成され、バインドエラーが発生します.この場合、コンパイラは賢い推定入力をT&とし、参照折りたたみをトリガーします.パラメータの最終タイプをT&と推定
上記の特性に基づいて、2番目のバージョンを書きました.
//          ,          
template < typename T >
T& quest( bool cond, T&& true_val, T&& false_val ) {
        if( cond ) return true_val;
        return false_val;
}

コンパイル結果:
void test1() {
        int i = 0, j = 0, k;
        quest( i > 0, i, j ) = 3;  // ok
        quest( i > 0, i, 2 ) = 3;  // error
        quest( i > 0, 1, j ) = 3;  // error
        quest( i > 0, 1, 2 ) = 3;  // ok
        k = quest( j > 0, i, j );  // ok
        k = quest( j > 0, i, 2 );  // error
        k = quest( i > 0, 1, j );  // error
        k = quest( i > 0, 1, 2 );  // ok
}

入力されたパラメータが左値と右値の場合、エラーは何が起こっているのか分かりますが、4番目がパスしました.つまり、左値参照が返されているので、2つの右値を入力しても、一時変数の左値参照がパスされます.
3そして上記cの特徴を思いついたので、戻り値をTと書きました.左の値が伝わったとき、コンパイラがTをリファレンスタイプに押し倒してくれたからです.
template < typename T >
T quest( bool cond, T&& true_val, T&& false_val ) {
        if( cond ) return true_val;
        return false_val;
}

これはすでに問題の90%を解決したと言える.最後に残った問題は、左の値と右の値が入ったらどうするかということです.私の解決策は2つの関数を増やすことです.
最終結果
template < typename T >
T quest( bool cond, T&& true_val, T&& false_val ) {
        if( cond ) return true_val;
        return false_val;
}

template < typename T >
T quest( bool cond, T& true_val, T&& false_val ) {
        if( cond ) return true_val;
        return false_val;
}

template < typename T >
T quest( bool cond, T&& true_val, T& false_val ) {
        if( cond ) return true_val;
        return false_val;
}

参照[1]http://en.cppreference.com/w/cpp/language/reference [2] http://www.th7.cn/Program/cp/201403/183896.shtml [3] http://www.2cto.com/kf/201311/260709.html