5

右辺値参照を特定のオブジェクトまたはその一時コピーにバインドするための最良の方法は何ですか?

A &&var_or_dummy = modify? static_cast<A&&>( my_A )
                         : static_cast<A&&>( static_cast<A>( my_A ) );

(このコードは最近のGCC 4.6では機能しません…以前は機能していたことを思い出しますが、現在は常にコピーを返します。)

最初の行で、左辺値からx値にstatic_cast変換します。my_A(C++0x§5.2.9/1-3)static_cast2行目の内側は左辺値から右辺値への変換を実行し、外側はこの値からx値を取得します。

名前付き参照は§12.2/5に従って条件付きで一時的にバインドされるため、これはサポートされているようです。同じトリックは、const参照を使用してC++03でも同じように機能します。

同じことをあまり冗長に書くこともできません。

A &&var_or_dummy = modify? std::move( my_A )
                         : static_cast<A&&>( A( my_A ) );

今でははるかに短くなっています。最初の省略形は疑わしいです:move単なる左辺値からx値から左辺値へのシャッフルではなく、オブジェクトに何かが起こっていることを示すことになっています。紛らわしいことに、関数呼び出しが一時から参照へのバインドを中断するため、のmove後に使用することはできません。:構文A(my_A)はおそらくより明確ですがstatic_cast、技術的にはCスタイルのキャストと同等です。

私はずっと行き、それを完全にCスタイルのキャストで書くこともできます:

A &&var_or_dummy = modify? (A&&)( my_A ) : (A&&)( A( my_A ) );

結局のところ、これがイディオムになるのであれば、それは便利である必要があり、static_castとにかく私を何からも実際に保護しているわけではありません。本当の危険はmy_Atrueケースに直接バインドできないことです。

一方、これは3回繰り返されるタイプ名によって簡単に支配されます。大きくて醜いtemplate-idに置き換えられた場合A、私は本当に本当のショートカットが欲しいと思います。

(5回表示されているにもかかわらず、1回だけ評価されることに注意してくださいV:)

#define VAR_OR_DUMMY( C, V ) ( (C)? \
  static_cast< typename std::remove_reference< decltype(V) >::type && >( V ) \
: static_cast< typename std::remove_reference< decltype(V) >::type && >   (  \
  static_cast< typename std::remove_reference< decltype(V) >::type >( V ) ) )

マクロはハックですが、それが最良の選択肢だと思います。xvalueを返すため、少し危険です。したがって、参照の初期化以外では使用しないでください。

私が考えていなかった何かがあるに違いありません…提案?

4

3 に答える 3

2

余分な関数呼び出しでこの混乱全体を避けてください:

void f(bool modify, A &obj) {
  return [&](A &&obj) {
    real();
    work();
  }(modify ? std::move(obj) : std::move(A(obj)));
}

それ以外の:

void f(bool modify, A &obj) {
  A &&var_or_dummy = /* ??? */;
  real();
  work();
}

どこでもラムダ、ラムダです!

于 2010-10-09T09:41:57.067 に答える
2

あなたのアプローチには2つの問題があります。

あなたは行動に依存しています

int   i = 0;
int&  j = true?      i  :      i;
int&& k = true? move(i) : move(i);
assert(&i == &j); // OK, Guaranteed since C++98
assert(&i == &k); // Does this hold as well?

現在の標準ドラフトN3126には5.16/4が含まれています。

[条件演算子の]2番目と3番目のオペランドが同じ値カテゴリーのglvalueであり、同じタイプである場合、結果はそのタイプと値カテゴリーになります。

これは、上記の2つの主張が成り立つべきだと私に思わせます。しかし、GCC 4.5.1を使用すると、2番目のものは失敗します。これはGCCのバグだと思います。

yさらに、次の例で参照されている一時オブジェクトの寿命を延ばすためにコンパイラーに依存しています。

A func();

A&& x = func();                   // #1
A&& y = static_cast<A&&>(func()); // #2

xぶら下がっているリファレンスにはなりませんが、私にはよくわかりませんy。一時的なものの寿命を延ばすという規則は、初期化子の式が純粋な右辺値である場合にのみ適用されると考えられます。少なくとも、これにより実装が大幅に簡素化されます。また、GCCはこれについて私に同意しているようです。2番目のケースでは、GCCは一時的なAオブジェクトの存続期間を延長しません。これは、あなたのアプローチにおけるぶら下がり参照の問題になります

更新: 12.2 / 5によると、一時オブジェクトの有効期間は、#1と#2の両方の場合に延長されることになっています。例外のリストにある箇条書きは、ここでは当てはまらないようです。繰り返しますが、GCCはこの点でバグがあるようです。

問題の簡単な解決策の1つは次のとおりです。

vector<A> tempcopy;
if (!modify) tempcopy.push_back(myA);
A& ref = modify ? myA : tempcopy.back();

または、ベクトルの代わりにboost::scoped_ptrを使用することもできます。

于 2010-10-09T16:18:17.130 に答える
0

xvalueの安全性の問題は、式内で使用するための代替手段を提供することである程度回避できます。問題は完全に異なります。xvalueの結果は必要なく、関数を使用できます。

template< typename T >
T &var_or_dummy( bool modify, T &var, T &&dummy = T() ) {
    if ( modify ) return var;
    else return dummy = var;
}

    maybe_get_result( arg, var_or_dummy( want_it, var ) );

ここで、タイプはデフォルトで構築可能である必要があり、ダミーは常に構築されます。コピーは条件付きで評価されます。私は、これをやりすぎたコードを本当に扱いたいとは思わない。

BoostOptionalは少し役に立ちます。CopyConstructible Tのみが必要です:

template< typename T >
T &var_or_dummy( bool modify, T &var,
                 boost::optional< T > &&dummy = boost::optional< T >() ) {
    if ( modify ) return var;
    else return dummy = var;
}

オプションは便利ですが、C++0xユニオンと一部重複しています。再実装するのはそれほど難しくありません。

template< class T >
struct optional_union {
    bool valid;
    union storage {
        T obj; // union of one non-POD member simply reserves storage

        storage() {} // uh, what could the constructor/destructor possibly do??
        ~storage() {}
    } s;

    optional_union() : valid( false ) {}
    optional_union &operator=( T const &in ) {
        new( &s.obj ) T( in ); // precondition: ! valid
        valid = true;
        return *this; 
    }
    ~optional_union()
        { if ( valid ) s.obj.~T(); }
};

template< typename T >
T &var_or_dummy( bool modify, T &var,
                 optional_union< T > &&dummy = optional_union< T >() ) {
    if ( modify ) return var;
    else return ( dummy = var ).s.obj;
}

クラスはこのoptional_unionアプリケーションにのみ十分です…明らかにそれはたくさん拡張される可能性があります。

于 2010-10-10T02:07:47.360 に答える