Reputation: 526
Can a function template be enabled whenever some expression is undefined (e.g., x
of type t
is not streamable to std::cout
). Something like
template<typename t>
auto f(const t &x)
-> typename enable_if_undefined<decltype(std::cout << x)>::type;
Using SFINAE, I only see how to enable if the expression is defined, but not how to do it if the expression is undefined.
Upvotes: 5
Views: 714
Reputation: 275906
template<class...>struct types{using type=types;};
namespace details {
template<template<class...>class Z, class types, class=void>
struct test_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct test_apply<Z,types<Ts...>,std::void_t<Z<Ts...>>>:
std::true_type
{};
};
template<template<class...>class Z, class...Ts>
using test_apply=details::test_apply<Z,types<Ts...>>;
is the metaprogramming boilerplate.
Then the actual use-case specific code is really clean. First, a simple decltype
alias:
template<class X>
using cout_result = decltype( std::cout << std::declval<X>() );
and then we apply the test to it:
template<class X>
using can_cout_stream = test_apply< cout_result, X >;
the goal here is to decouple the metaprogramming boilerplate from the actual use.
template<class T>
std::enable_if_t<!can_cout_stream<const T&>{}>
f(const T& x)
I liberally use C++14 features to make things a touch cleaner. They can all be easily (re)implemented in C++11 if your compiler doesn't have them.
template<bool b, class T=void>
using enable_if_t=typename std::enable_if<b,T>::type;
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
should cover it.
Upvotes: 3
Reputation: 56921
You need a helper which provides a boolean value you can reverse:
template<typename, typename=void>
struct has_cout
: std::false_type {};
template<typename T>
struct has_cout<T, decltype(std::cout << std::declval<T>(),void())>
: std::true_type {};
template<typename T>
auto f(const T& x)
-> typename std::enable_if<!has_cout<T>::value>::type;
Upvotes: 7