2012-08-26 7 views
10

このプログラムをC++ 11コンパイラでコンパイルすると、ベクタは関数から移動されません。3値演算子の結果が右辺値ではない

#include <vector> 
using namespace std; 
vector<int> create(bool cond) { 
    vector<int> a(1); 
    vector<int> b(2); 

    return cond ? a : b; 
} 
int main() { 
    vector<int> v = create(true); 
    return 0; 
} 

このようなインスタンスを返すと、そのインスタンスが返されます。

if(cond) return a; 
else return b; 

ここにはdemo on ideoneがあります。

gcc 4.7.0とMSVC10で試しました。どちらも同じように動作します。
これはなぜですか?
3つの演算子の型は、returnステートメントが実行される前に評価されるため、左辺値です。この時点で、aとbはまだxvaluesではない(期限切れになる)。
この説明は正しいですか?

これは標準の欠陥ですか?
これは明らかに意図された動作ではなく、私の意見では非常に一般的なケースです。

+0

"第2オペランドと第3オペランドが同じ値カテゴリのglvaluesで、同じ型を持つ場合、結果はその型と値のカテゴリになります[...]"§5.16/ 4 – Mat

+0

いずれの例もrvaluesまたはxvaluesを含みません。しかし、なぜコピーが移動の代わりに行われるのかという興味深い質問です。 – aschepler

答えて

8

は、関連する標準引用符です:

12.8段落32:

コピーの省略return文の

  • [...]以下の状況で許可されています式がクラスの戻り型を持つ関数で、式が関数またはcatch節のパラメータ以外の非揮発性自動オブジェクトの名前であり、sがAME CV-修飾されていない関数の戻り型とタイプ、コピー/移動操作は、関数の戻り値
  • に直接自動オブジェクトを構築することによって省略することができる[条件付きthrow ING、】
  • [源は、Aであります
  • ]条件と、一時[たときの値によって catch ING、条件付き]

パラグラフ33:

コピー操作の抽出基準が満たされた場合、またはソースオブジェクトが関数パラメータであり、コピーされるオブジェクトがlvalueで指定されているという事実を除いて保存された場合、コピーのコンストラクタを選択するオーバーロード解決あたかもオブジェクトが右値によって指定されたかのように行われる。オーバーロードの解決が失敗した場合、または選択されたコンストラクタの最初のパラメータの型がオブジェクトの型(おそらくcv修飾されたもの)への参照値でない場合は、オブジェクトを左辺値と見なして再度オーバーロードの解決が行われます。[注:この2段階の過負荷解決は、コピーエリミッションが発生するかどうかに関係なく実行する必要があります。 elisionが実行されなかった場合に呼び出されるコンストラクタを決定し、選択されたコンストラクタは、呼び出しが省略されてもアクセス可能でなければなりません。 - エンドノート]

return (cond ? a : b);での式は単純な変数名ではないので、それはコピーの省略または右辺値処理の対象ではありません。多少不幸なことかもしれませんが、コンパイラ実装の期待の頭痛を覚えるまで、一度に少し例を伸ばすことは想像しやすいでしょう。

安全であるとわかっている場合は、戻り値を明示的にstd::moveと明示することで、もちろんこれを回避することができます。

+0

これは、段落33がlvalueを戻り値に移動できる唯一のものであることを示しているため、段落33はこのreturn文には適用されません(適格でないelisionでは関数のパラメータではない)は、移動できないことを意味しますか?私はこれを信じていません、私は推論に従ったかどうか確信していません。 –

+0

@SteveJessop:そうだね。 12.8p32-33がなければ、唯一の正しい動作は戻り値にコピーすることです。 p32を使用し、p33を使用しない場合、実装はコピーをコピーするかエリートするかを選択できます。 p32とp33の両方(現実的)では、実装は移動を移動または抹消するかのどちらかを選択できます。この実装では、コピーと移動のどちらかを選択することはできません。オーバーロードの解決とこの特定の例外は、省略されなければ発生します。 – aschepler

7

これはあなたのコードは、パラメータが暗黙的に移動されません

return ternary(cond, a, b); 

、あなたはそれを明示的に作成する必要があるように、それは

return cond ? std::move(a) : std::move(b); 

は、関数として三項演算子を考えてみましょ修正します。ここで

+1

'return std :: move(cond?a:b); 'も働いている。それは同等ですか? –

+0

@AaronMcDaid:はい。 – GManNickG

+0

これは正解です。 – Walter