none
unique_ptrの初期化

    質問

  • 次のOK行、Complile error行の違いが分かりません。

    教えてください。(vc++2010)

    std::unique_ptr<AClass> CreateElement()
    {
    	return std::unique_ptr<AClass>( new AClass(100) );
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
    	std::unique_ptr<AClass> pElement = CreateElement();// OK
     
    	std::unique_ptr<AClass> pElement2 =  pElement;// Complie error
     
     
     
    	return 0;
    }


    • 編集済み Brillia 2017年11月16日 7:21
    2017年11月16日 5:48

回答

  • C++11で導入された右辺値参照・ムーブセマンティクスという考え方が影響していますので、これを理解することが近道です。

    C言語およびC++言語で = 代入演算子はコピーを行う概念であり、

    j = i;

    はiの値をコピーし変数jに代入することは理解されていることと思います。さてここで従来から存在していた std::auto_ptr に目を向けますと

    std::auto_ptr<int> ai;
    std::auto_ptr<int> aj;
    aj = ai;

    もちろん通常の変数と同様にソースコード上は代入が行えていますが、実態としてはオブジェクトの所有権がaiからajに移動しています。これは従来の = はコピーであるべきとの概念と矛盾しています。

    これを解決するために冒頭で紹介した右辺値参照とムーブセマンティクスが登場し、それを用いたstd::unique_ptrが定義されいました。
    質問文で説明しますと、CreateElement()関数が返したオブジェクトは所有権を失い変数pElementに移ります。しかし次の行では、コピーなのか移動なのか意図が読み取れないためコンパイルエラーを引き起こすようライブラリ設計が為されています。

    std::unique_ptr<AClass> pElement2 = std::move(pElement);

    と記述することで明示的に移動を行う意思表示ができ、コンパイルも通ります。

    具体的なライブラリの設計としては、std::auto_ptrではoperator=(std::auto_ptr&)が定義されているのに対し、std::unique_ptrではoperator=(std::unique_ptr&)が削除されており代わりにoperator=(std::unique_ptr&&)が定義されています。

    なお、最新の仕様ではstd::auto_ptrは廃止済みとされており、VC++ 2017では削除されています(マクロ定義で復活させることもできますが)。

    • 回答としてマーク Brillia 2017年11月17日 4:45
    2017年11月16日 9:16

すべての返信

  • unique_ptrは「そのオブジェクトの所有権が唯一であるように振る舞う」様に実装されたポインタであるので、
    コピー(operator =)等は使えないように実装されているのではないでしょうか。

    所有権を移転するためには、move等を使えば良いとおもうのですが、どうでしょう。

    std::unique_ptr<AClass> pElement2 =  std::move( pElement);// Complie error=>OK

    なら通るのではないでしょうか。

    by 仲澤@失業者 ・・・発言者が表示されないので(vv;)
    2017年11月16日 6:53
  • ご返事ありがとうございます。

    質問の趣旨が分かりにくくすみません。

    所有権を、ユニークにするため、operator=は、隠蔽されているようで、
    Compile error側が、エラーになるのは理解できるのですが、

    なぜ関数の戻り値の時は、なぜコンパイルができるのだろうかと、疑問に思い質問しました。

    関数の戻り値での代入は、値渡しとなっているのでしょうか?



    ※発言者名が表示されなかったのはなぜなのか?はわかりませんが、投稿を行ったところ表示されました。。。


    • 編集済み Brillia 2017年11月16日 7:34
    2017年11月16日 7:33
  • あんまり厳密に説明できませんが、ざっくりと説明すると、
    最初の行の場合、関数の戻り値なので右辺値(名無しの一時的インスタンス)という扱いだからではないでしょうか。
    ステップ実行してみると、コンストラクタ

    explicit unique_ptr(pointer _Ptr)

    が実行されてそれが、pElement になります。その他のコンストラクタやオペレータ等は実行されません。

    エラー行をトレースすることはできませんが、
    この行の場合、pElementは既に名称のある実態であるので、右辺値ではなく左辺値(実態あり)と判断できます。
    従って=は直前のクラスのメンバでありpElementはその引数と解釈されます。
    この場合、たぶん、デフォルトコンストラクタ unique_ptr() を実行したのち、
    そのメンバーである operator =(const unique_ptr<AClass> )を呼ぶようにコードされてますが、
    そのオペレータはprivateなので使用不可ですよ、のエラーになってしまうのだと思います。

    ただし、このあたりは実装によるので、VS2010の場合はこんな感じかも、程度の解釈にしといてほしいです。

    2017年11月16日 8:46
  • すみません。あまり詳しくはないのですが、C++0X の NRVO の機能が働いて、関数の戻り値ではエラーが起こらないのではないでしょうか?

    std::unique_ptr<AClass> CreateElement()
    {
    	return std::unique_ptr<AClass>( new AClass(100) );
    }
    
    std::unique_ptr<AClass> pElement = CreateElement();// OK
    
    // 上記は、無名オブジェクトとしてそのまま渡されるので
    
    std::unique_ptr<AClass> pElement = std::unique_ptr<AClass>( new AClass(100) );
    
    // と書いたことと同一視できる

    参考サイト:
    http://d.hatena.ne.jp/gintenlabo/20110125/1295991902
    https://stackoverflow.com/questions/4316727/returning-unique-ptr-from-functions

    2017年11月16日 9:06
  • C++11で導入された右辺値参照・ムーブセマンティクスという考え方が影響していますので、これを理解することが近道です。

    C言語およびC++言語で = 代入演算子はコピーを行う概念であり、

    j = i;

    はiの値をコピーし変数jに代入することは理解されていることと思います。さてここで従来から存在していた std::auto_ptr に目を向けますと

    std::auto_ptr<int> ai;
    std::auto_ptr<int> aj;
    aj = ai;

    もちろん通常の変数と同様にソースコード上は代入が行えていますが、実態としてはオブジェクトの所有権がaiからajに移動しています。これは従来の = はコピーであるべきとの概念と矛盾しています。

    これを解決するために冒頭で紹介した右辺値参照とムーブセマンティクスが登場し、それを用いたstd::unique_ptrが定義されいました。
    質問文で説明しますと、CreateElement()関数が返したオブジェクトは所有権を失い変数pElementに移ります。しかし次の行では、コピーなのか移動なのか意図が読み取れないためコンパイルエラーを引き起こすようライブラリ設計が為されています。

    std::unique_ptr<AClass> pElement2 = std::move(pElement);

    と記述することで明示的に移動を行う意思表示ができ、コンパイルも通ります。

    具体的なライブラリの設計としては、std::auto_ptrではoperator=(std::auto_ptr&)が定義されているのに対し、std::unique_ptrではoperator=(std::unique_ptr&)が削除されており代わりにoperator=(std::unique_ptr&&)が定義されています。

    なお、最新の仕様ではstd::auto_ptrは廃止済みとされており、VC++ 2017では削除されています(マクロ定義で復活させることもできますが)。

    • 回答としてマーク Brillia 2017年11月17日 4:45
    2017年11月16日 9:16