Корректность кода с заведомо неверными шаблонными функциями-членами
struct A {
template<class T>
void foo() {
static_assert(sizeof(T) == 0);
}
};
#include <type_traits>
struct B {
template<class T>
std::enable_if_t<sizeof(T) == 0, void>
foo() {
;
}
};
struct C {
template<class>
operator auto() {
return 42;
}
};
int main() {
auto a = A{};
auto b = B{};
auto c = C{};
}
Почему этот код компилируется без предупреждений в clang, компилируется с предупреждением в gcc, не компилируется в msvc?
Разве то, что шаблонные функции-члены не вызываются, не делает этот код корректным? Касается ли это шаблонных операторов? Касается ли это только шаблонного оператора преобразования?
Предупреждение в gcc:
warning: use of 'auto' in member template conversion operator can never be deduced
Ошибка в msvc:
error C3177: you cannot have a conversion function to a type that contains 'auto'
Ответы (2 шт):
A и B - ill-formed, no diagnostic required из-за [temp.res]/8.1. (Код неправильный, но ошибка или предупреждение не гарантируются.)
C - ill-formed из-за [class.conf.fct]/6. (Должен вызывать ошибку или предупреждение, иначе компилятор не соответствует стандарту.)
В языке предусмотрена масса правил, согласно которым шаблон может делать программу невалидной (с диагностикой или без) еще до инстанцирования, или даже в отсутствии такового, или даже если сам шаблон валидный.
- К объявлению и определению шаблона применяются обычные проверки синтаксиса в несколько сокращенном объеме, насколько позволяет двухфазный поиск имен. В этом примере еще до инстанцирования компилятор имеет возможность определить, что
T::barне является именем типа, что делает конструкциюT::bar x{};и весь шаблон невалидным:
template<typename T> void
foo(void) { T::bar x{}; }
Объявление шаблонного оператора преобразования, в котором параметр шаблона должен выводится, как раз относится к этой группе - такой оператор не разрешен согласно 11.4.7.2.6 Conversion functions [class.conv.fct]. Поведение gcc и clang в этом случае неконформное.
К шаблонам также применяется рад специальных правил, описанных в 13.8 Name resolution [temp.res] и делающих программу невалидной, но при этом не требующих диагностики от компилятора. В эту категорию попадает пример с
B, в котором никакой вариант инстанцированияB::fooне будет валидным.К шаблонам применяются обычные правила, касающиеся единственности определения. В этом примере поведение программы не определено, хотя шаблоны сами по себе валидные:
// одна единица трансляции
template<typename T> class
Foo{ public: int m_x{}; };
Foo<int> x{};
// другая единица трансляции
template<typename T> class
Foo{ protected: int m_x{}; };
Foo<int> x{};
- К шаблонам применяются ряд специальных правил, описанных в 13.7.6.1 Function template overloading [temp.over.link] и делающих программу невалидной, но при этом не требующих диагностики от компилятора, хотя шаблоны сами по себе валидны и не инстанцируются. В этом примере шаблоны
fявляются функционально эквивалентными, но различны:
template<int x>
class A{};
template<int I> void
f(A<I>, A<I+2>) {}
template<int I> void
f(A<I>, A<I+1+1>) {}