First you need to make your SFINAE-friendly landbat.
#define RETURNS(...)\
noexcept(noexcept(__VA_ARGS__))\
->decltype(__VA_ARGS__)\
{ return __VA_ARGS__; }
const auto add = [](const auto a, const auto b) RETURNS( a + b );
const auto subtract = [](const auto a, const auto b) RETURNS( a - b );
Now adding and peeling can be tested in the context of SFINAE.
namespace details {
template<class, class, class...>
struct can_invoke:std::false_type {};
template<class F, class...Args>
struct can_invoke<F, std::void_t< std::result_of_t< F&&(Args&&...) > >, Args... >:
std::true_type
{};
}
template<class F, class...Args>
using can_invoke_t = details::can_invoke<F, Args...>;
template<class F, class...Args>
constexpr can_invoke_t< F, Args... >
can_invoke( F&&, Args&&... ){ return {}; }
and we are ready:
template <typename A, typename B>
void foo(A a, B b) {
if constexpr ( can_invoke( add, a, b ) ) {
std::cout << "result of add: " << add(a, b) << std::endl;
}
if constexpr ( can_invoke( subtract, a, b ) {
std::cout << "result of subtract: " << subtract(a, b) << std::endl;
}
}
this C ++ 14; atC ++ 11 it's more uncomfortable in C ++ 17 , ( , , add std::invoke).
++ 17 :
template<class F>
constexpr auto invoke_test( F&& ) {
return [](auto&&...args) ->
can_invoke_t<F, decltype(args)...>
{ return {}; };
}
template <typename A, typename B>
void foo(A a, B b) {
if constexpr ( invoke_test( add )( a, b ) ) {
std::cout << "result of add: " << add(a, b) << std::endl;
}
if constexpr ( invoke_test( subtract )( a, b ) {
std::cout << "result of subtract: " << subtract(a, b) << std::endl;
}
}
invoke_test , " , ".