Detect if an operator exists and can be called in C ++ (given static_asserts)

Given 2 types of T and U , I want to determine if operator * can be called between these objects (i.e., you can write t * u , where T is of type T and U is of type U )

I use the C ++ discovery identifier , but since it is not yet available in my compiler, I myself implemented this as

 struct nonesuch { nonesuch() = delete; ~nonesuch() = delete; nonesuch(nonesuch const&) = delete; void operator=(nonesuch const&) = delete; }; namespace detail { template <class Default, class AlwaysVoid, template<class...> class Op, class... Args> struct detector { using value_t = std::false_type; using type = Default; }; template <class Default, template<class...> class Op, class... Args> struct detector<Default, std::void_t<Op<Args...>>, Op, Args...> { using value_t = std::true_type; using type = Op<Args...>; }; } // namespace detail template <template<class...> class Op, class... Args> using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t; template< template<class...> class Op, class... Args > constexpr bool is_detected_v = is_detected<Op, Args...>::value; 

Now I have a helper like this:

 template <typename T, typename U> using multiply = decltype(std::declval<T>() * std::declval<U>()); 

and determine if it is callable, I call

 bool can_multiply = is_detected_v<multiply, T, U> 

This is almost excellent, for example, it prints 1, 0, as expected

 std::cout << is_detected_v<multiply, int, int> << std::endl; std::cout << is_detected_v<multiply, std::vector<int>, std::vector<int>> << std::endl; 

But now I have a class

 template<typename T> class A { }; template<typename T> A<T> operator*(const A<T>&, const A<T>&) { static_assert(!std::is_same<bool, T>::value); return A<T>(); } 

here A<bool> cannot be multiplied by A<bool> , but my code detects what is possible

 std::cout << is_detected_v<multiply, A<bool>, A<bool>> << std::endl; // 1 A<bool>() * A<bool>(); // does't compile 

So my question is how to fix my code so that it does not detect methods when they are static_asserted out? I suppose I can replace static_assert with some sfinae, but I don't want to (because I don't have access, and besides, static_asserts have better error messages).

+6
source share
1 answer

So my question is how to fix my code so that it does not detect methods when they are static_asserted out?

You just can't. This is only one of the drawbacks of static_assert - there is no way to verify the validity of an operation. This is because static_assert does not occur in the "immediate context" of the operator* instance, so SFINAE is not applied - it will always be a tough mistake.

I suppose I can replace static_assert with some sfinae, but I don't want to (because I don't have access, and besides, static_asserts have better error messages).

Sorry. But this is basically a compromise. SFINAE and type checking, or static_assert and more clear errors. (Of course, in this case you could just write not the template A<bool> operator*(A<bool> const&, A<bool> const&) , but, perhaps, beyond this point).

+7
source

All Articles