2016-09-11 13 views
6

Учитывая 2 типа T и U Я хочу, чтобы обнаружить, можно ли назвать operator * между теми объектами (т.е. есть возможность написать t * u где t имеет типа T и u имеет тип U)Detect существует ли оператор и вызываемые в C++ (с учетом static_asserts)

Я использую c++ detection idiom, но так как он еще не доступен в моем компиляторе я реализовал это сам, как этот

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; 

Сейчас я есть такой помощник:

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

и обнаружить, является ли это вызываемая я называю

bool can_multiply = is_detected_v<multiply, T, U> 

Это почти нормально, например, это печатает 1, 0, как и ожидалось

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

Но теперь у меня есть класс

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>(); 
} 

he повторно A<bool> не может быть умножена на A<bool>, но мой код определяет, что это возможно

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

Итак, мой вопрос, как исправить мой код, чтобы не обнаружить методы, когда они static_asserted вне дома? Я полагаю, что я могу заменить static_assert на некоторые sfinae, но я не хочу (потому что у меня нет доступа, и, кроме того, static_asserts имеют лучшие сообщения об ошибках).

ответ

7

Итак, мой вопрос заключается в том, как исправить мой код, чтобы не обнаруживать методы, когда они статические_запускаются?

Вы просто не можете. Это лишь один из недостатков static_assert - нет возможности проверить достоверность операции. Это потому, что static_assert не происходит в «непосредственном контексте» экземпляра operator*, поэтому SFINAE не применяется - это всегда будет жесткая ошибка.

Я полагаю, что я могу заменить static_assert на некоторые sfinae, но я не хочу (потому что у меня нет доступа, и, кроме того, static_asserts имеют лучшие сообщения об ошибках).

Сочувствую. Но это в основном компромисс. SFINAE и проверка типов, или static_assert и более ясные ошибки. (Конечно, в этом случае вы могли бы просто написать не-шаблон A<bool> operator*(A<bool> const&, A<bool> const&), но это, вероятно, не считая того).

+0

Объясняя, почему может помочь (SFINAE проверка должна быть сделана во время разрешения перегрузки, заставляя каждый компилятор компилировать все функции и определить, есть ли у него какой-либо незаконный код до определения, которые перегружают назвать бы как трудно для компиляторов, и может привести к в удивительном поведении (я предпочел 'void *' over 'T *', потому что ваш код 'T * 'содержит ошибку, приводящую к ошибке в уровнях 2-го уровня, вложенных вглубь). Проверка SFINAE является компромиссом, и даже там MSVC падает на них * hard *. – Yakk