std :: unique_ptrを関数に渡すにはどうすればよいですか


100

どうすればstd::unique_ptr関数にを渡すことができますか?次のクラスがあるとしましょう。

class A
{
public:
    A(int val)
    {
        _val = val;
    }

    int GetVal() { return _val; }
private:
    int _val;
};

以下はコンパイルされません。

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);

    return 0;
}

なぜstd::unique_ptr関数にを渡せないのですか?確かに、これが構成の主な目的ですか?または、C ++委員会は、生のCスタイルのポインターにフォールバックして次のように渡すことを意図していましたか?

MyFunc(&(*ptr)); 

そして何よりも奇妙なことに、なぜこれがそれを渡すのに良い方法なのですか?それはひどく矛盾しているようです:

MyFunc(unique_ptr<A>(new A(1234)));

7
生のCスタイルのポインターが所有されていない生のポインターである限り、生のCスタイルのポインターに「フォールバック」しても問題はありません。'ptr.get()'を書くことをお勧めします。ただし、null可能性が必要ない場合は、参照が推奨されます。
クリス・ドリュー2015年

回答:


147

ここには基本的に2つのオプションがあります。

参照によりスマートポインタを渡す

void MyFunc(unique_ptr<A> & arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);
}

スマートポインタを関数の引数に移動します

この場合、アサーションが保持されることに注意してください。

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(move(ptr));
    assert(ptr == nullptr)
}

@VermillionAzure:参照している機能は通常、コピー不可と呼ばれ、一意ではありません。
ビル・リンチ

1
ありがとう。この場合、インスタンスを作成し、インスタンスに対していくつかのアクションを実行してから、所有権を他の誰かに譲渡したいと思いました。これは、move()に最適のようです。
user3690202 2015年

7
unique_ptr関数がそこから移動するかどうかにかかわらず、参照によってのみを渡す必要があります。そして、それは右辺値の参照である必要があります。所有権のセマンティクスについて何も要求せずにオブジェクトを監視するには、A const&またはのような参照を使用しますA&
ポテトスワッター2015年

13
CppCon2014に関するHerbSuttersの講演を聞いてください。彼は、unique_ptr <>への参照を渡すことを強くお勧めしません。本質は、所有権を扱うときは、unique_ptr <>やshared_ptr <>のようなスマートポインターを使用する必要があるということです。ここでは、スライドに見つけることができwww.youtube.com/watch?v=xnqTKD8uD64を参照してください github.com/CppCon/CppCon2014/tree/master/Presentations/...
schorsch_76

2
@Furihr:ptr移動後の価値を示す方法にすぎません。
ビル・リンチ

28

あなたはそれを価値によって渡している、それはコピーを作ることを意味する。それはあまりユニークではないでしょう?

値を移動することもできますが、それはオブジェクトの所有権とその存続期間の制御を関数に渡すことを意味します。

MyFuncの呼び出しの存続期間にわたってオブジェクトの存続期間が存在することが保証されている場合は、を介して生のポインターを渡すだけptr.get()です。


17

なぜunique_ptr関数にを渡せないのですか?

unique_ptr移動コンストラクターはありますが、コピーコンストラクターがないため、これを行うことはできません。規格によれば、移動コンストラクターは定義されているがコピーコンストラクターが定義されていない場合、コピーコンストラクターは削除されます。

12.8クラスオブジェクトのコピーと移動

..。

7クラス定義でコピーコンストラクターが明示的に宣言されていない場合は、暗黙的に宣言されます。クラス定義がムーブコンストラクターまたはムーブ代入演算子を宣言している場合、暗黙的に宣言されたコピーコンストラクターは削除済みとして定義されます。

unique_ptrを使用して、を関数に渡すことができます。

void MyFunc(std::unique_ptr<A>& arg)
{
    cout << arg->GetVal() << endl;
}

そしてあなたが持っているようにそれを使用してください:

または

void MyFunc(std::unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

そしてそれを次のように使用します:

std::unique_ptr<A> ptr = std::unique_ptr<A>(new A(1234));
MyFunc(std::move(ptr));

重要な注意点

2番目のメソッドを使用する場合はptr、の呼び出し後にポインタの所有権がないことに注意してくださいstd::move(ptr)

void MyFunc(std::unique_ptr<A>&& arg)void MyFunc(std::unique_ptr<A>& arg)どちらも参照であるため、同じ効果があります。

最初のケースでptrは、への呼び出し後もポインタの所有権がありますMyFunc


5

MyFunc所有権を取ることはありません、それが持っている方が良いでしょう。

void MyFunc(const A* arg)
{
    assert(arg != nullptr); // or throw ?
    cout << arg->GetVal() << endl;
}

以上

void MyFunc(const A& arg)
{
    cout << arg.GetVal() << endl;
}

本当に所有権を取得したい場合は、リソースを移動する必要があります。

std::unique_ptr<A> ptr = std::make_unique<A>(1234);
MyFunc(std::move(ptr));

または、r値参照を直接渡します。

MyFunc(std::make_unique<A>(1234));

std::unique_ptr 所有者が1人だけであることを保証するための意図的なコピーはありません。


この回答には、最初の2つのケースでMyFuncへの呼び出しがどのように見えるかが欠落しています。を使用しているのか、関数にptr.get()渡すだけなのかわからない場合はptr
taylorstine

MyFuncr値参照を渡すための意図された署名は何ですか?
JamesHirschorn20年

@JamesHirschorn:void MyFunc(A&& arg)... r値の参照を取る
Jarod42

@ Jarod42それは私が推測したことですがtypedef int A[]MyFunc(std::make_unique<A>(N))を使用すると、コンパイラエラーが発生します:エラー:タイプ 'std :: _ MakeUniq <int []> :: __ array' {の式からのタイプ 'int(&&)[]'の参照の無効な初期化別名 'std :: unique_ptr <int []、std :: default_delete <int [] >>'}g++ -std=gnu++11最近のもので十分ですか?
JamesHirschorn20年

:Jarod42私はideoneとC ++ 14の故障を確認した@ ideone.com/YRRT24
ジェームズHirschorn

4

なぜunique_ptr関数にを渡せないのですか?

std::unique_ptr<>コピーで構築することはできませんが、コピーではできません。

確かに、これが構成の主な目的ですか?

とりわけ、(ではなく)一意の所有権std::unique_ptr<>を明確にマークするように設計されてい ます。std::shared_ptr<>

そして何よりも奇妙なことに、なぜこれがそれを渡すのに良い方法なのですか?

その場合、コピー構築がないためです。


ご回答有難うございます。興味深いことに、それを渡す最後の方法がコピーコンストラクターを使用しない理由を説明できますか?unique_ptrのコンストラクターを使用すると、スタック上にインスタンスが生成され、コピーコンストラクターを使用してMyFunc()の引数にコピーされると思いましたか?私の記憶はこの分野では少し曖昧であることは認めますが。
user3690202 2015年

2
これは右辺値であるため、代わりにmove-constructorが呼び出されます。あなたのコンパイラは確かにこれをとにかく最適化しますが。
ニールク2015年

0

unique_ptrは一意の所有権のためであるため、引数として渡したい場合は試してください

MyFunc(move(ptr));

しかし、その後の状態はptrmainなりますnullptr


4
「未定義になります」-いいえ、nullになるか、unique_ptrかなり役に立たなくなります。
TC 2015年

0

std::unique_ptr<T>皆さんがおっしゃるように、unique_ptrコピーできないため、関数に値として渡すことは機能しません。

これはどうですか?

std::unique_ptr<T> getSomething()
{
   auto ptr = std::make_unique<T>();
   return ptr;
}

このコードは機能しています


そのコードが機能する場合、私の推測では、unique_ptrをコピーしているのではなく、実際には移動セマンティクスを使用して移動しています。
user36902 0220

私が推測する戻り値最適化(RVO)かもしれません
NouemanKhalikine20年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.