web-dev-qa-db-ja.com

メンバー関数でstd :: bindを使用して、この引数にオブジェクトポインタを使用するかどうか?

std::bindを使用してメンバー関数をバインドする場合、最初の引数はオブジェクトthisポインターです。ただし、オブジェクトをポインタとして渡す場合と渡さない場合の両方で機能します。

たとえば、次のプログラムを参照してください。

#include <iostream>
#include <functional>

struct foo
{
    void bar(int v) { std::cout << "foo::bar - " << v << '\n'; }
};

int main()
{
    foo my_foo;

    auto f1 = std::bind(&foo::bar, my_foo, 1);
    auto f2 = std::bind(&foo::bar, &my_foo, 2);

    f1();
    f2();
}

ClangとGCCはどちらもこれを文句なしにコンパイルし、結果は両方のバインドで機能します。

 foo :: bar-1 
 foo :: bar-2 

私は仕様(セクション20.8.9)に頭を包み込もうとしてきましたが、それは私にははっきりとは程遠い場所の1つです。

正しいのは1つだけですか、両方とも正しいですか?

62

両方とも正しいです。 20.8.9.1.2から20.8.2に転送して、bindへの呼び出しの要件と効果を説明します。 20.8.2は:

20.8.2要件[func.require]

1 [〜#〜] invoke [〜#〜](f, t1, t2, ..., tN)を次のように定義します。

(t1.*f)(t2, ..., tN)fがクラスTのメンバー関数へのポインターであり、t1T型のオブジェクトまたはT型のオブジェクトへの参照の場合Tから派生した型のオブジェクトへの参照。

((*t1).*f)(t2, ..., tN)fがクラスTのメンバー関数へのポインターであり、t1が前の項目で説明したタイプのいずれでもない場合。

t1.*fおよびN == 1およびfがクラスTのメンバーデータへのポインターであり、t1T型のオブジェクトまたはT型のオブジェクトへの参照の場合Tから派生した型のオブジェクトへの参照。

(*t1).*fN == 1およびfがクラスTのメンバーデータへのポインターであり、t1が前の項目で説明したタイプのいずれでもない場合。

f(t1, t2, ..., tN)その他のすべての場合。

最初の2つのオプションは、参照とポインターの両方を許可します。

ここで注意すべき重要なことは、言葉遣いがnotを使用してプレーンポインターに制限することです。 std::shared_ptrまたは他のスマートポインターを使用して、バインドされている間インスタンスを存続させ、std::bindが間接参照されるため、それが何であれ(もちろん可能です)、それは引き続きt1で動作します。

49
Daniel Frey

正解に追加する(両方のフォームが許可される)。

関数引数宣言と同様の2つのバインディングオプションを考えます。これは、「値によって渡される」または「参照によって渡される」可能性があります。

の場合 f1(別名my_foo「値による」)結果は、my_foo結合点を通過します。これは、特にmy_foo進化します。 「値による」バインディングには、コピーコンストラクターへの(複数の)呼び出しの追加の「コスト」があります。

2
rytis