したがって、これにアプローチするには多くの方法があり、さまざまな量の作業が必要です。それらのどれも完全に自明ではありません。
T::operator()
最初に、渡された型のシグネチャが type であるかどうかを調べたりチェックしたりすることで、そのシグネチャをアンパックできますR (*)(Args...)
。
次に、署名の同等性を確認します。
2 番目のアプローチは、通話の互換性を検出することです。次のような特性クラスを記述します。
template<typename Signature, typename F>
struct call_compatible;
これは、std::true_type
またはが戻り値に変換可能std::false_type
かどうかに依存します。この場合、これで問題は解決します。decltype<F&>()( declval<Args>()... )
Signature
ここで、オーバーロードしている 2 つの署名に互換性がある場合は、さらに作業を行う必要があります。std::function<void(double)>
つまり、 andがある場合を想像してみてくださいstd::function<void(int)>
-- それらはクロスコール互換です。
どれが「最良」のものかを判断するには、私の以前の質問を参照してください。ここでは、多数の署名を取得して、どれが最も一致するかを見つけることができます。次に、戻り値の型チェックを行います。これは複雑になっています!
call_compatible
このソリューションを使用すると、最終的には次のようになります。
template<size_t>
struct unique_type { enum class type {}; };
template<bool b, size_t n=0>
using EnableIf = typename std::enable_if<b, typename unique_type<n>::type>::type;
class Foo {
template<typename Lambda, EnableIf<
call_compatible<void(), Lambda>::value
, 0
>...>
Foo( Lambda&& f ) {
std::function<void()> fn = f;
// code
}
template<typename Lambda, EnableIf<
call_compatible<int(), Lambda>::value
, 1
>...>
Foo( Lambda&& f ) {
std::function<int()> fn = f;
// code
}
};
これは、他のソリューションの一般的なパターンです。
これが への最初の刺し傷call_compatible
です:
template<typename Sig, typename F, typename=void>
struct call_compatible:std::false_type {};
template<typename R, typename...Args, typename F>
struct call_compatible<R(Args...), F, typename std::enable_if<
std::is_convertible<
decltype(
std::declval<F&>()( std::declval<Args>()... )
)
, R
>::value
>::type>:std::true_type {};
それはまだテスト/コンパイルされていません。