Как избежать этого предложения ложного в шаблоне SFINAE?
Поэтому я хочу написать автоматический!=
:
template<typename U, typename T>
bool operator!=(U&& u, T&& t) {
return !( std::forward<U>(u) == std::forward<T>(t) );
}
но это невежливо1, Вот я и пишу
// T() == U() is valid?
template<typename T, typename U, typename=void>
struct can_equal:std::false_type {};
template<typename T, typename U>
struct can_equal<
T,
U,
typename std::enable_if<
std::is_convertible<
decltype( std::declval<T>() == std::declval<U>() ),
bool
>::value
>::type
>: std::true_type {};
который является классом черт типа, который говорит "являетсяt == u
допустимый код, который возвращает тип, преобразуемый вbool
».
Так что я улучшаю!=
:
template<typename U, typename T,
typename=typename std::enable_if<can_equal<T,U>::value>::type
>
bool operator!=(U&& u, T&& t) {
return !( std::forward<U>(u) == std::forward<T>(t) );
}
и теперь это только допустимое переопределение, если==
существует. К сожалению, это немного жадный
struct test {
};
bool operator==(const test&, const test&);
bool operator!=(const test&, const test&);
как это будет в значительной степени каждыйtest() != test()
а не выше!=
будучи призванным Я думаю, что это не желательно - я бы скорее назвал явное!=
чем автоматическая пересылка в==
и отрицать.
Итак, я пишу этот класс черт:
template<typename T, typename U,typename=void>
struct can_not_equal // ... basically the same as can_equal, omitted
какие тесты, еслиT != U
действует.
Затем мы увеличиваем!=
следующее:
template<typename U, typename T,
typename=typename std::enable_if<
can_equal<T,U>::value
&& !can_not_equal<T,U>::value
>::type
>
bool operator!=(U&& u, T&& t) {
return !( std::forward<U>(u) == std::forward<T>(t) );
}
который, если разобрать его, говорит: «это предложение ложно» -operator!=
существует междуT
а такжеU
тогда и только тогдаoperator!=
не существует междуT
а такжеU
.
Неудивительно, что каждый компилятор, который я тестировал segfaults, когда его кормил. (clang 3.2, gcc 4.8 4.7.2 intel 13.0.1).Я подозреваю, что то, что я делаю, является незаконным, но я хотел бы видеть стандартную ссылку. (редактировать: то, что я делаю, является незаконным, потому что оно вызывает неограниченное рекурсивное расширение шаблона, как определение того,!=
применяется требует, чтобы мы проверили, если мой!=
применяется. Версия, указанная в комментариях, с#if 1
даёт ощутимую ошибку).
Но мой вопрос: есть ли способ убедить мое переопределение, основанное на SFINAE, игнорировать «себя» при принятии решения, потерпеть неудачу или нет, или каким-то образом избавиться от проблемы со ссылками на себя? Или понизьте приоритет моегоoperator!=
достаточно низко, поэтому любой явный!=
выигрывает, даже если это не так хорошо сочетается?
Тот, который не проверяет "!=
«не существует» работает достаточно хорошо, но недостаточно хорошо для меня, чтобы быть настолько невежливым, чтобы внедрить его в глобальное пространство имен.
Цель - любой код, который компилируется без моей "магии"!=
делает то же самое, когда моя "магия"!=
вводится. Если и только если!=
в противном случае является недействительныма также bool r = !(a==b)
хорошо сформирован, если моя "магия"!=
ворваться.
$43но это невежливо44$template<typename U, typename T> bool operator!=(U&& u, T&& t)
SFINAE подумает, что каждая пара типов имеет действительный!=
между ними. Затем, когда вы пытаетесь на самом деле позвонить!=
, он создается и не компилируется. Кроме того, вы топаете поbool operator!=( const foo&, const foo& )
функции, потому что вы лучше подходите дляfoo() != foo()
а такжеfoo a, b; a != b;
, Я считаю, что делать оба этих невежливых.