Код неверно сформирован, и этот экспериментальный gcc 9.0.0 неверен, чтобы его игнорировать.
Главное, что нужно понять о правиле SFINAE, это то, что оно применяется при формировании недопустимого типа или выраженияпри подстановке параметра шаблона функции в аргументы шаблона по умолчанию для других параметров шаблона функции или в тип функции, как описано во введении в разделе [temp.deduct] ;или при определении, соответствует ли частичная специализация шаблона класса или какая частичная специализация является наиболее специализированной.Но в вашем коде недопустимый тип формируется при замене параметра LA
шаблона класса, а не параметра шаблонов функций.
Также актуален параграф [temp.inst] / 2:
Неявное создание экземпляра специализации шаблона класса вызывает
неявное создание объявления, но не определений не-удаленные функции-члены класса, классы-члены, перечисления членов-областей, статические члены-данные, шаблоны членов и друзья;и
неявная реализация определений удаленных функций-членов, перечисления членов с незаданной областью и анонимные объединения-члены.
неявная реализацияСпециализация шаблона класса не приводит к неявной реализации аргументов по умолчанию или noexcept-спецификатора s функций-членов класса.
Поскольку определения x
и y
в пределахmain
требует, чтобы типы Node<lazy, AND>
и Node<nonlazy, OR>
были завершены, шаблон класса должен быть создан для этих двух специализаций.И создание шаблона класса означает создание объявлений шаблона функции, поэтому в каждом случае программа плохо сформирована, поскольку это объявление содержит независимый недопустимый тип.
Примечание: как вы упомянули, if constexpr
, пожалуй, самый простой способ решить эту проблему в C ++ 17.(Публичная функция может просто вызывать одну из двух закрытых функций с разными именами, если вы хотите, чтобы реализации были отдельными.) Но в C ++ 20 будут введены «ограничения» для шаблонов и функций, которые обеспечат еще более приятное решение:
template<laziness LA, logic LO>
struct Node{
void printLaziness () const requires (LA==lazy) {
std::cout << "lazy" << std::endl;
}
void printLaziness () const requires (LA==nonlazy) {
std::cout << "non-lazy" << std::endl;
}
};
В отличие от трюков SFINAE, вполне допустимо иметь функцию-член, ограниченную выражением, эквивалентным false
, или которое иначе никогда не может быть выполнено.Это означает, что он никогда не будет считаться подходящим кандидатом для разрешения перегрузки.