Reputation: 3336
I'm trying to make a compile time generics example. I've made it enforce that the methods exists, but I can't figure out how to enforce the return type:
#include <type_traits>
template<class...> struct voider { using type = void; }
template<class... T> using void_t = typename voider<T...>::type;
template<class T, class = void> struct add_able : std::false_type {};
template<class T> struct add_able
< T, void_t < std::is_same < decltype(std::declval<T>().addOne()), void >,
std::is_same < decltype(std::declval<T>().subOne()), void > > >
: std::true_type {};
class A { public: int addOne(); void subOne(); } // compiles and runs
class B { public: void addOne(int); void subOne(); } // fails
class C { public: void addOne(); void subOne(); } // compiles and runs as expected
class D { public: void subOne(); } // fails
This ensures that there is a addOne()
and subOne()
method but the return types are completely arbitrary. How do I enforce this?
Upvotes: 3
Views: 149
Reputation: 48527
std::is_same<T>
is a type. Its instantiation will never fail (triggering a substitution failure) as you'd expect. You have to wrap it in a std::enable_if
and read the value that std::is_same<T>
returns by accessing a nested ::value
static member.
template<class T, class = void> struct add_able : std::false_type {};
template<class T> struct add_able
< T, void_t < typename std::enable_if<
// ~~~~~~~~~~~~~^
std::is_same < decltype(std::declval<T&>().addOne()), void >::value
// ~~~~~~^
&& std::is_same < decltype(std::declval<T&>().subOne()), void >::value
// ~^^~ ~~~~~~^
>::type
// ~~~~~^
>
>
: std::true_type {};
Upvotes: 3