Я писал частичную специализацию для класса, когда параметр шаблона является производным от определенного типа. Моя реализация выглядит следующим образом:
struct base {};
struct derived: public base {};
template <typename T, typename=void>
struct foo {
foo() { std::cout << "Version 1" << std::endl; }
};
template <typename T>
struct foo <T, typename std::enable_if<std::is_base_of<base, T>::value>::type> {
foo() { std::cout << "Version 2" << std::endl; }
};
Это работает, как и ожидалось: когда я создаю экземпляр foo<int>, конструктор печатает Version 1, а когда я создаю экземпляр foo<derived>, конструктор печатает Version 2.
Теперь я кое-что тестировал и хотел отключить частичную специализацию, поэтому я просто изменил ее на -
template <typename T>
struct foo <T, typename std::enable_if<false>::type> {
foo() { std::cout << "Version 2" << std::endl; }
};
Я просто заменил std::is_base_of<base, T>::value на константу false. Но на этот раз компилятор начал выдавать предупреждения типа std::enable_if<false, void> не имеет type.
Почему оно так себя ведет? Почему здесь не работает SFINAE? Почему параметр std::enable_if ДОЛЖЕН зависеть от параметра шаблона?
Почему здесь не работает SFINAE?
SFINAE расшифровывается как «сбой замены не является ошибкой». Если ваш аргумент для std::enable_if не зависит, то замены нет, и поэтому имеет смысл, что он не будет применяться.
В частности, стандарт говорит, что программа неправильно сформирована, диагностика не требуется, если
гипотетическая реализация шаблона сразу после его определения будет неправильно сформирована из-за конструкции, которая не зависит от параметра шаблона, [...]
(из [temp.res.general]/8.4) проекта N4868 после C++20)
Std::enable_if<false>::type не зависит и неправильно сформирован сразу после определения вашего шаблона.
Ну, это ответ на вопрос! Спасибо за ссылку на язык из стандарта. Означает ли это, что если я создам тип template <typename T> always_false { constexpr bool value = false} и буду использовать always_false<T>::value внутри enable_if, это будет нормально? Или этот шаблон тоже всегда искажен?
@AjayBrahmakshatriya Можно поспорить, делает ли timsong-cpp.github.io/cppwp/n4868/temp.res.general#8.1 все еще IFNDR, но на практике все должно быть хорошо, да.
@PatrickRoberts См. комментарий выше.
@ user17732522 попался! Я буду использовать sizeof(T) == 0. Надеюсь, нигде в стандарте не сказано, что размер шрифта не может быть равен 0.
Хорошо, конечно, технически это неправильно, и вы не хотели бы оставлять готовый код таким (как упоминалось в вопросе, это было временно для целей тестирования), но «диагностика не требуется», потому что на практике требуется, чтобы компилятор проверьте, что в каждом шаблоне определение непомерно дорого, и ни одна известная мне реализация не будет явно запрещать это.
@PatrickRoberts О, понятно! Я думаю, что в качестве последней попытки стандартного соответствия я мог бы использовать std::is_same<a_sentinel_type_nobody_should_use, T>::value, чтобы он даже компилировался. И да, я бы все равно не оставил это в кодовой базе, это было просто для тестирования чего-то
В качестве продолжения я бы использовал sizeof(T) == 0. Это всегда false в стандартном коде.