1. ホーム
  2. c++

[解決済み] 関数からunique_ptrを返す

2022-03-21 23:04:23

質問

unique_ptr<T> はコピー構築を許可せず、代わりに移動セマンティクスをサポートします。しかし、私は unique_ptr<T> 関数から返された値を変数に代入しています。

#include <iostream>
#include <memory>

using namespace std;

unique_ptr<int> foo()
{
  unique_ptr<int> p( new int(10) );

  return p;                   // 1
  //return move( p );         // 2
}

int main()
{
  unique_ptr<int> p = foo();

  cout << *p << endl;
  return 0;
}

上のコードはコンパイルして意図したとおりに動作します。では、どうして 1 がコピーコンストラクタを呼び出してコンパイラーエラーになることはないのでしょうか?もし、私が行 2 を使うのは理にかなっていると思います。 2 も同様に動作しますが、そうする必要はありません)。

C++0xではこの例外を認めていますが unique_ptr というのも、戻り値は一時的なオブジェクトであり、関数が終了すると同時に破棄されるため、戻り値のポインタの一意性が保証されるからです。これはどのように実装されているのか気になります。コンパイラで特別にケース分けされているのか、それとも言語仕様に何か他の条項があり、これを悪用しているのでしょうか?

解決方法は?

<ブロッククオート

は、言語仕様の他の条項を悪用したものなのでしょうか?

はい、12.8 §34 と §35 を参照してください。

特定の条件を満たした場合、実装はクラスオブジェクトのコピー/移動の構築を省略することが許される[...]。 このコピー/移動操作の省略は、以下のように呼ばれる。 コピーエリジョン が許可される[...]。 は、クラスの戻り値の型を持つ関数の return 文で許可されています。 の名前である場合、その式は 不揮発性自動オブジェクト 関数の戻り値の型と同じ cv-unqualified 型の [...]...

コピー操作のエリシオン条件を満たし、コピーされるオブジェクトがlvalueで指定されている場合。 コピー先のコンストラクタを選択するための過負荷解消が最初に行われます。 オブジェクトがr値で指定されている場合と同じように .


最悪の場合、つまりC++11、C++14、C++17でエリジオンを使用しない場合、return文の名前付き値はrvalueとして扱われるため、ここでは値で返すことがデフォルトの選択であることをもう一点だけ付け加えたいと思います。 したがって、たとえば次のような関数は -fno-elide-constructors フラグ

std::unique_ptr<int> get_unique() {
  auto ptr = std::unique_ptr<int>{new int{2}}; // <- 1
  return ptr; // <- 2, moved into the to be returned unique_ptr
}

...

auto int_uptr = get_unique(); // <- 3

コンパイル時にフラグを立てると、この関数内で2手(1、2)が発生し、その後に1手(3)が発生します。