Xlea
Xlea

Reputation: 526

Enable a function template in C++ whenever an expression is undefined

Can a function template be enabled whenever some expression is undefined (e.g., xof 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

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

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

Daniel Frey
Daniel Frey

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;

Live example

Upvotes: 7

Related Questions