1

std::mem_fn手巻きのラッパー関数ができることをできないユースケースに遭遇しました。メソッドのクラスではなく、暗黙的にそれに変換可能な型でラッパー関数が使用されている場合に発生します。

#include <functional>

struct A
{
};

struct B
{
    B(A);  // implicit conversion from A to B
    void foo() const;
};


auto foo1 = std::mem_fn(&B::foo);     // std::mem_fn

void foo2(const B& b) { b.foo(); }    // hand-rolled wrapper

int main()
{
    A a;
    foo1(a);  // doesn't work
    foo2(a);  // works fine
}

foo1 への呼び出しのコンパイラ エラーは次のとおりです (GCC 4.8 の場合)。

In file included from test.cpp:1:0:
functional: In instantiation of '_Res std::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)const>::_M_call(_Tp&, const volatile void*, _ArgTypes ...) const [with _Tp = A; _Res = void; _Class = B; _ArgTypes = {}]':
functional:608:42:   required from '_Res std::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)const>::operator()(_Tp&, _ArgTypes ...) const [with _Tp = A; _Res = void; _Class = B; _ArgTypes = {}]'
test.cpp:21:11:   required from here
functional:586:13: error: no match for 'operator*' (operand type is 'A')
  { return ((*__ptr).*__pmf)(std::forward<_ArgTypes>(__args)...); }
             ^

std::mem_fnこのユースケースが手巻きラッパーの場合と同じように機能するように実装することは可能でしたか?

4

1 に答える 1

5

はい、可能ですが、C++標準が指定する方法ではありませんmem_fn

標準では、[func.require] で次のように定義されている場所をfoo1(a)呼び出します。INVOKE(&B::foo, a)

次 のINVOKE (f, t1, t2, ..., tN)ように定義します。_ _ — f がクラスのメンバー関数へのポインターであり、前の項目で説明されている型のいずれでもない場合。 — ...
(t1.*f)(t2, ..., tN)fTt1TTT
((*t1).*f)(t2, ..., tN)Tt1

aは class のオブジェクトでも、または から派生したクラスBへの参照でもないため、あなたのケースは最初の箇条書きの条件を満たしていません。BB((*a).*f)()

スマートポインタを使用できるようにするために、このように定義されています。

auto foo1 = std::mem_fn(B::foo);
auto p = std::make_shared<B>();
foo1(p);

の定義( 、、および呼び出しラッパーを作成するライブラリの他の部分でINVOKEも使用される) は、ラップされたメンバーへのポインターを呼び出すときに、最初の引数が でない場合、何らかの種類であると想定されることを意味します。ポインターの、逆参照されます。これは、andだけでなく、 andなどの何も知らない型でも機能することを意味します。bindfunctionasynct1Tstd::shared_ptrstd::unique_ptrstd::mem_fnboost::shared_ptrMyVeryOwnSmartPtr

t1コードを機能させるには、 が aTまたは から派生した型ではTなくtrue の場合に処理するケースを追加してis_convertible<T>::value、 を呼び出すことT(t1).*f)()ができますが、仕様が複雑になり、場合によっては望ましくない結果になる可能性があります。

あなたの「ラッパー」はその引数の暗黙的な変換を強制しますが、タイプ のスマートポインタまたは右辺値を処理できません。Bこれらは両方とも でサポートされていmem_fnます。関数を呼び出すためにAオブジェクトを変換する特定のケースがある場合は、それを実行してください。ジェネリックテンプレートは適切ではありませんが、より柔軟でジェネリックであり、他の多くの状況で機能します。Bmem_fn

(注意: の定義は、引数を逆参照するのと同じ方法でオブジェクトをINVOKE逆参照するため、実際には欠陥があります。 http://cplusplus.github.com/LWG/lwg-active.html#2219で修正を提案しましたが、それはあなたの例には影響しません。)std::reference_wrappera

于 2013-02-08T09:41:26.307 に答える