Корректность кода с заведомо неверными шаблонными функциями-членами

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 шт):

Автор решения: HolyBlackCat

A и B - ill-formed, no diagnostic required из-за [temp.res]/8.1. (Код неправильный, но ошибка или предупреждение не гарантируются.)

C - ill-formed из-за [class.conf.fct]/6. (Должен вызывать ошибку или предупреждение, иначе компилятор не соответствует стандарту.)

→ Ссылка
Автор решения: user7860670

В языке предусмотрена масса правил, согласно которым шаблон может делать программу невалидной (с диагностикой или без) еще до инстанцирования, или даже в отсутствии такового, или даже если сам шаблон валидный.

  • К объявлению и определению шаблона применяются обычные проверки синтаксиса в несколько сокращенном объеме, насколько позволяет двухфазный поиск имен. В этом примере еще до инстанцирования компилятор имеет возможность определить, что 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>) {}
→ Ссылка