namespace details {
template<template<class...>class Z, class, class...Ts>
struct can_apply : std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,void, Ts...>
template<class Sig>
using can_invoke = can_apply< std::result_of_t, Sig >;
現在特性クラスcan_invoke
を持っています。
あなたは署名を渡します。署名が有効な呼び出しであれば真実で、そうでなければ偽ります。私はこのようなenable_if_t
、void_t
とresult_of_t
のようないくつかのC++ 11を使用
template<class T>
std::enable_if_t< !can_invoke<T()> >
use_this(T obj) {
obj->do_stuff();
}
template<class T>
std::enable_if_t< can_invoke<T()> >
use_this(T func) {
use_this(func());
}
。すべてはC++ 11で簡単に記述でき、簡単に検索できます。
無効な署名が渡されたときのresult_of
の動作がC++ 14で変更されました。 C++ 11では、SFINAEに対応する必要はありません。次のように我々はC++ 11にinvoke_result_r
でstd::result_of_t
を置き換えることができます。
template<class Sig, class=void>
struct invoke_result {};
template<class Sig>
using invoke_result = typename invoke_result<Sig>::type;
template<class F, class...Args>
struct invoke_result<F(Args...),
decltype(void(std::declval<F>()(std::declval<Args>()...)))
> {
using type = decltype(std::declval<F>()(std::declval<Args>()...));
};
これは今SFINAEフレンドリーです。
can_apply
は、C++ 20のis_detected
に似ていますが、短くポイントまでです。
MSVCのような非C++ 11コンパイラでこれを実行することは実用的ではないことに注意してください。しかし、タグのディスパッチと同様のことができます。