Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect whether operator exists and callable in c++ (considering static_asserts)

Given 2 types T and U I want to detect whether it's possible to call operator * between those to objects (i.e is it possible to write t * u where t is of type T and u is of type U)

I'm using c++ detection idiom but since it's not yet available in my compiler I implemented it myself like this

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 such helper:

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

and to detect whether it's callable I call

bool can_multiply = is_detected_v<multiply, T, U>

It's almost fine , for example this 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 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> can't be multiplied by A<bool> but my code detects that it's 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 to not detect methods when they 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).

like image 338
RiaD Avatar asked Sep 11 '16 17:09

RiaD


1 Answers

So, my question is, how to fix my code to not detect methods when they static_asserted out?

You simply can't. That's just one of the downsides of static_assert - there is no way to externally verify the validity of an operation. That's because static_assert doesn't happen in the "immediate context" of the instantiation of operator*, and so SFINAE doesn't apply - it will always be a hard error.

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).

I sympathize. But that's basically the trade-off. SFINAE and type checking, or static_assert and clearer errors. (Of course in this case you could just write a non-template A<bool> operator*(A<bool> const&, A<bool> const&) but that's probably besides the point).

like image 100
Barry Avatar answered Sep 20 '22 18:09

Barry