12

bar別の関数のパラメーター ( foo1/ ) のコンテキストで、オーバーロードされた関数 ( ) のアドレスを解決しようとしていますfoo2

struct Baz {};

int bar() { return 0; }
float bar(int) { return 0.0f; }
void bar(Baz *) {}

void foo1(void (&)(Baz *)) {}

template <class T, class D>
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {}

int main() {
    foo1(bar);      // Works
    foo2<Baz>(bar); // Fails
}

の型を明示的foo1に指定する に問題はありません。bar

ただし、foo2の 1 つを除くすべてのバージョンに対して SFINAE を介してそれ自体を無効barにすると、次のメッセージが表示されてコンパイルに失敗します。

main.cpp:19:5: fatal error: no matching function for call to 'foo2'
    foo2<Baz>(bar); // Fails
    ^~~~~~~~~
main.cpp:15:6: note: candidate template ignored: couldn't infer template argument 'D'
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {}
     ^
1 error generated.

C++ では、オーバーロードされた関数のアドレスを解決すると同時に、テンプレート引数の推定を実行できないことを理解しています。

それが原因ですか?foo2<Baz>(bar);(または同様のもの) をコンパイルする方法はありますか?

4

2 に答える 2

1

ある種の一般的な答えはここにあります:渡された関数ポインタの型をオーバーロードする式SFINAE

実際のケースでは、型特性を使用する必要はありません。またはdecltype()、古き良きオーバーロード解決により、最も適切な関数が選択され、それが「引数」と「戻り値の型」に分割されます。可能なすべての呼び出し規約を列挙するだけです

// Common functions
template <class T, typename R> void foo2(R(*)(T*)) {}

// Different calling conventions
#ifdef _W64
template <class T, typename R> void foo2(R(__vectorcall *)(T*)) {}
#else
template <class T, typename R> void foo2(R(__stdcall *)(T*)) {}
#endif

// Lambdas
template <class T, class D>
auto foo2(const D &d) -> void_t<decltype(d(std::declval<T*>()))> {}

それらをテンプレート化された構造でラップすると便利な場合があります

template<typename... T>
struct Foo2 {
    // Common functions
    template <typename R> static void foo2(R(*)(T*...)) {}
    ...
};
Zoo2<Baz>::foo2(bar);

constただし、メンバー関数には修飾子 ( 、volatile&&)があるため、より多くのコードが必要になります。

于 2015-11-20T02:38:07.863 に答える