web-dev-qa-db-ja.com

shared_ptrを参照または値で渡す必要がありますか?

関数がshared_ptr(boostまたはC++ 11 STLから)を受け取るとき、それを渡しますか?

  • const参照による:void foo(const shared_ptr<T>& p)

  • または値で:void foo(shared_ptr<T> p)

私はそれが高速になると思うので、私は最初の方法を好むでしょう。しかし、これは本当に価値がありますか、または追加の問題がありますか?

あなたが選んだ理由を教えていただけますか?.

240
Danvil

この質問は、 C++ and Beyond 2011 のセッションで Ask Us Anything セッション中にScott、AndreiおよびHerbによって議論され、回答されました。 4分34秒から見る shared_ptrパフォーマンスと正確さ

すぐに、値で渡す理由はありません、目標がオブジェクトの所有権を共有することでなければ(例えば、異なるデータ構造間または異なる間でスレッド)。

上記以外に、Scott Meyersが説明したトークビデオで説明されているように、移動最適化を行うことができますが、それは使用可能なC++の実際のバージョンに関連しています。

GoingNative 2012 カンファレンスの インタラクティブパネル:何でも聞いてください! の間に、この議論の主要な更新が行われました。特に 22:5

203
mloskot

Herb Sutter's take

ガイドライン:所有権の共有や譲渡など、スマートポインター自体を使用または操作する場合を除き、スマートポインターを関数パラメーターとして渡さないでください。

ガイドライン:by-value shared_ptrパラメーターを使用して、関数がヒープオブジェクトの所有権を保存および共有することを明示します。

ガイドライン:非const shared_ptr&パラメーターは、shared_ptrを変更する場合にのみ使用してください。コピーを取得して所有権を共有するかどうかわからない場合にのみ、const shared_ptr&をパラメーターとして使用します。それ以外の場合は、代わりにwidget *を使用します(または、null可能でない場合は、widget&)。

78
acel

個人的には、const参照を使用します。関数呼び出しのために参照カウントを再度デクリメントするために参照カウントをインクリメントする必要はありません。

60
Evan Teran

const参照で渡すと、高速になります。コンテナに保存する必要がある場合は、ref。カウントは、コピー操作によって自動的に増分されます。

34

以下のコードを実行しました。fooshared_ptrconst&で、もう一度fooshared_ptrを値で取得しました。

void foo(const std::shared_ptr<int>& p)
{
    static int x = 0;
    *p = ++x;
}

int main()
{
    auto p = std::make_shared<int>();
    auto start = clock();
    for (int i = 0; i < 10000000; ++i)
    {
        foo(p);
    }    
    std::cout << "Took " << clock() - start << " ms" << std::endl;
}

インテル2015コア2クアッド(2.4 GHz)プロセッサーでVS2015、x86リリースビルドを使用

const shared_ptr&     - 10ms  
shared_ptr            - 281ms 

値によるコピーバージョンは、桁違いに遅くなりました。
現在のスレッドから関数を同期的に呼び出す場合は、const&バージョンを選択してください。

20
tcb

C++ 11以降、あなたはそれをconst&の値によるより頻繁に取る必要があります。

(基礎となる型Tではなく)std :: shared_ptrを使用している場合は、それを使用して何かを実行したいので、そうしています。

コピーするどこかにしたい場合は、const&で取得して後でコピーするのではなく、コピーで取得し、std :: moveで内部的に移動する方が理にかなっています。これは、関数を呼び出すときにstd :: moveがshared_ptrを順番に呼び出すオプションを呼び出し元に許可するため、インクリメントとデクリメントの操作のセットを節約できるからです。か否か。つまり、関数の呼び出し元は、関数の呼び出し後にstd :: shared_ptrが必要かどうかを、移動するかどうかによって決定できます。 const&を渡す場合、これは達成できません。したがって、値で取得することをお勧めします。

もちろん、呼び出し側の両方が彼のshared_ptrをより長く必要とし(したがってstd :: moveできない)、関数にプレーンコピーを作成したくない場合(弱いポインターが必要な場合、またはたまにしか必要ない場合)いくつかの条件に応じてコピーするには)、const&がまだ望ましい場合があります。

たとえば、あなたがする必要があります

void enqueue(std::shared<T> t) m_internal_queue.enqueue(std::move(t));

以上

void enqueue(std::shared<T> const& t) m_internal_queue.enqueue(t);

この場合、常に内部でコピーを作成するため

13
Cookie

アトミックなインクリメントとデクリメントが行われるshared_copyコピー操作の時間コストがわからないため、CPU使用率の問題がはるかに大きくなりました。アトミックなインクリメントとデクリメントにはそれほどコストがかかるとは思っていませんでした。

私のテスト結果に続いて、int32のアトミックな増分および減分は、非アトミックな増分および減分よりも2または40回かかります。 Windows 8.1を搭載した3GHz Core i7で入手しました。前者の結果は競合が発生しない場合に発生し、後者は競合の可能性が高い場合に発生します。アトミック操作は最後にハードウェアベースのロックであることに注意してください。ロックはロックです。競合が発生するとパフォーマンスが低下します。

これを経験して、私は常にbyval(shared_ptr&)よりもbyref(const shared_ptr&)を使用しています。

1
Hyunjik Bae