web-dev-qa-db-ja.com

ポインタを返すときに、ポインタを自動的にunique_ptrに変換できないのはなぜですか?

例を通して私の質問を提起させてください。

#include <memory>

std::unique_ptr<int> get_it() {
        auto p = new int;
        return p;
}

int main() {
        auto up ( get_it() );
        return 0;
}

これはコンパイルに失敗し、次のエラーが発生します。

a.cpp:5:9: error: could not convert ‘p’ from ‘int*’ to ‘std::unique_ptr<int>’
  return p;
         ^

生のポインタから一意のポインタへの自動変換がここにないのはなぜですか?そして、私は代わりに何をすべきですか?

動機:所有権を明確にするためにスマートポインターを使用することは良い習慣であると思われることを理解しています。この場合はint*として、どこかから(私が所有している)ポインターを取得しています。私は(私が思うに)それをunique_ptrに入れたいと思っています。


コメントしたり、独自の回答を追加したりすることを検討している場合は、 提案N4029でこれを可能にするためのハーバートサターの議論 に対処してください。

15
einpoklum

答えは2つあります。 OPの自己回答を含む他のすべての回答は、その半分しか扱っていませんでした。

次の理由により、ポインタを自動的に変換できません。

  • ポインタからのunique_ptrの-​​ コンストラクタexplicitと宣言されているため、コンパイラは明示的なコンテキストでのみ考慮します。これは、偶発的な危険な変換を防ぐために行われます。この場合、unique_ptrは、プログラマーの知らないうちにポインターを乗っ取って削除する可能性があります。一般に、unique_ptrだけでなく、偶発的な変換を防ぐために、すべての単一引数コンストラクターをexplicitとして宣言することをお勧めします。
  • returnステートメントは、標準では暗黙的なコンテキストと見なされるため、明示的なコンストラクターは適用できません。この決定が正しいかどうかについて継続的な議論があり、 EWG問題114 に反映され、いくつかの提案へのリンクが含まれています:ハーブサッターによってreturnを明示するための提案の2つのバージョン( N4029N4074 )、およびそうしないと主張する2つの「応答」: N4094 HowardHinnantおよびVilleVoutilainenおよび N4131 =FilipRoséenによる。いくつかの議論と世論調査の後、問題はNADとしてクローズされました-欠陥ではありません。

現在、いくつかの回避策があります。

return std::unique_ptr<int>{p};

または

return std::unique_ptr<int>(p);

C++ 14では、関数の戻り値の型の自動推定も使用できます。

auto get_it() {
    auto p = new int;
    return std::unique_ptr<int>(p);
}

更新:2番目のポイントの委員会の問題へのリンクを追加しました。

16
Ilya Popov

ネイキッドポインタからのunique_ptrの暗黙的な構築は、エラーが発生しやすいためです。

明示的に作成するだけです。

std::unique_ptr<int> get_it() {
        auto p = new int;
        return std::unique_ptr<int>(p);
}
7
Stas

なぜならstd::unique_ptrがポインターの所有権を取得し、生のポインターdeletedを誤って取得したくないことは間違いありません。

それが可能であれば、次のようにします。

void give_me_pointer(std::unique_ptr<int>) { /* whatever */ }

int main() {
    int *my_int = new int;
    give_me_pointer(my_int);
    // my_int is dangling pointer
}
4
GingerPlusPlus

変換またはキャストには、適切なキャスト演算子(ソースタイプ)またはコンストラクター(ターゲットタイプ)が必要なためです。この場合、それは 必須 _unique_ptr_の次のコンストラクターです。

_explicit unique_ptr( pointer p );
_

明示的なキーワード があります。 get_it()は暗黙の変換を試みますが、これはexplicitが防ぎます。代わりに、 @ Stas および@VincentSavardで提案されているように、_unique_ptr_を明示的に作成する必要があります。

_std::unique_ptr<int> get_it() {
        auto p = new int;
        return std::unique_ptr<int>(p);
}
_

または、_unique_ptr_を1回だけ言いたい場合...

_auto get_it() {
        auto p = new int;
        return std::unique_ptr<int>(p);
}
_
2
einpoklum

危険だから。

C++ 14のstd::make_unique()を使用します。

std::unique_ptr<int> get_it()
{
    auto p = std::make_unique<int>();
    return p;
}
0