2012-07-15 6 views
14

gccから奇妙なエラーが発生しました。問題をより明確にするために、次のサンプルコードを作成しました。基本的には、私はそのコピーコンストラクタとコピー代入演算子をプライベートにして、誤って呼び出すのを防ぐためにクラスを定義しています。ベクトル:: push_backは、移動コンストラクタが用意されていますが、コピーコンストラクタを使用することを主張しています

#include <vector> 
#include <cstdio> 
using std::vector; 

class branch 
{ 
public: 
    int th; 

private: 
    branch(const branch& other); 
    const branch& operator=(const branch& other); 

public: 

    branch() : th(0) {} 

    branch(branch&& other) 
    { 
    printf("called! other.th=%d\n", other.th); 
    } 

    const branch& operator=(branch&& other) 
    { 
    printf("called! other.th=%d\n", other.th); 
    return (*this); 
    } 

}; 



int main() 
{ 
    vector<branch> v; 
    branch a; 
    v.push_back(std::move(a)); 

    return 0; 
} 

このコードはコンパイルすると期待していますが、gccで失敗します。実際にはgccは "branch :: branch(const branch &)は私的である"と訴えています。予想通り

代入演算子の作品、私はメインのボディを(置き換える場合以降)

branch a; 
branch b; 
b = a; 

ではコンパイルして実行します。

これはgccの正しい動作ですか?もしそうなら、上記のコードに何が問題なのですか? 私にはどんな提案も役に立ちます。ありがとうございました!

+0

gcc-4.6.1で動作します。 –

+0

私はgcc 4.7.1-2を使用していました。 4.6.1を試してみよう。ありがとう! – BreakDS

+2

私はN3242を読んだので、このコードは許されるべきです(しかし、ムーブコンストラクタが例外をスローした場合、プログラムは未定義の動作をします)。 – aschepler

答えて

16

"noexcept"を移動コンストラクタの宣言に追加してみてください。

標準を引用することはできませんが、gccの最近のバージョンでは、コピーコンストラクタをパブリックにするか、または移動コンストラクタを "noexcept"と宣言する必要があります。 "noexcept"修飾子に関係なく、コピーコンストラクタをpublicにすると、実行時に期待どおりに動作します。

+3

コピーコンストラクタをpublicにすると、オブジェクトはコピーされます。これは明らかに避けようとしているものです。いずれにしても、移動コンストラクタ 'noexcept'を作成するのが正しい解です。つまり+1です。 – ildjarn

+0

両方のあなたにありがとう、noexceptとパブリックコピーコンストラクタは両方とも正しいです。しかし、コピーコンストラクタをプライベートにすることはできません。 – BreakDS

+0

@BreakDS:移動コンストラクタが 'noexcept'の場合、コピーコンストラクタをプライベートにすることができます(正しい標準ライブラリ実装を前提とします)。 – ildjarn

9

gcc 4.7はが間違っていましたこのコードを拒否するにはcorrected in gcc 4.8でした。

vector<T>::push_backのための完全な標準準拠の動作は次のとおりです。

  • のみコピーコンストラクタなし移動コンストラクタがある場合は、push_backはその引数をコピーし、強い例外安全保証を与えるだろう。つまり、ベクトル記憶域の再割り当てによってトリガされた例外によってpush_backが失敗した場合、元のベクトルは変更されずにそのまま使用できます。これはC++ 98で知られている動作であり、それが後続する混乱の理由でもあります。
  • Tnoexcept移動コンストラクタがある場合、push_backになります。その引数からに移動し、強力な例外保証を行います。ここに驚きはありません。
  • ないnoexceptで、コピーコンストラクタもあり移動コンストラクタがある場合は、push_back意志コピーオブジェクトとは、強い例外安全保証を与えます。一見するとこれは予期せぬことです。 push_backはここに移動できますが、これは強力な例外保証を犠牲にしてのみ可能になります。 C++ 98からC++ 11にコードを移植し、そのタイプを移動可能にすると、既存のpush_back呼び出しの動作が静かに変更されます。この落とし穴を回避し、C++ 98コードとの互換性を維持するために、C++ 11は遅いコピーに戻っています。これはgcc 4.7の振る舞いのすべてです。しかしもっとあります...
  • noexceptない移動コンストラクタはあるが、まったくコピーコンストラクタ場合 - つまり、要素のみを移動し、コピーされないことができます - push_backは、移動を実行しませんが、が強い例外安全保証を与えることはありません。これはgcc 4.7が間違っていたところです。 C++ 98では、移動可能ではあるがコピーできない型に対しては、push_backはありません。ここで強力な例外安全性を犠牲にしても、既存のコードが破られることはありません。これが許可され、元のコードは実際には合法的なC++ 11です。

push_backcppreference.comを参照してください:

例外がスローされた場合、この関数は何の効果(強い 例外保証を)持っていません。

Tの移動コンストラクタがnoexceptではなく、 コピーコンストラクタにアクセスできない場合、vectorはthrowing move コンストラクタを使用します。それがスローされた場合、保証は放棄され、効果は 未指定です。

またはC++ 11標準(私が追加重視)からもう少し複雑§23.3.6.5:新しいサイズが古い容量よりも大きい場合

は、再配分を引き起こします。 再割り当てが行われない場合、 より前のすべてのイテレータと参照は有効なままです。コピーコンストラクタによって 以外に例外がスローされた場合は、コンストラクタ、代入演算子、または の代入演算子をTまたはそのInputIterator演算子で移動します。 は無効です。 非CopyInsertable Tの移動コンストラクタによって例外がスローされた場合、エフェクトは未定義です。

それとも、(約0時42分00秒で興味深い部分で午前0時30分20秒から始まる)、Scott Meyer's Going Native 2013 talkを読んで好きではない場合。

関連する問題