G++ и clang++ различное поведение со специализацией шаблона для автоматического аргумента

Играя с C++ 17 аргументами шаблона auto, я столкнулся с другим несогласием g++/клана g++.

Учитывая следующий простой код

template <auto>
struct foo;

template <int I>
struct foo<I>
 { };

int main ()
 {
   foo<42l> f42; // <--- long constant, not int constant

   (void)f42; // avoid the "unused variable" warning
 }

Я вижу, что клан g++ (8.0.0, к примеру) компилирует код, где g++ (9.2.0, к примеру) выдает следующую ошибку

prog.cc: In function 'int main()':
prog.cc:12:13: error: aggregate 'foo<42> f42' has incomplete type and cannot be defined
   12 |    foo<42l> f42;
      |             ^~~

Оба компилятора компилируются, если мы используем константу int вместо константы long

  foo<42>  f42;  // compile with both clang++ and g++

Итак, у меня есть два вопроса для языковых слоев C++

(1) законно ли, в C++ 17, специализировать шаблон, объявленный получающим параметр шаблона auto, для значения определенного типа (как специализация foo в моем коде)?

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

Вопрос (2) почти такой: правый клан g++ или g++?

Ответы

Ответ 1

Здесь немного другое воспроизведение, которое не основано на неполных типах:

template <auto> struct foo { static constexpr int value = 0; };
template <int I> struct foo<I> { static constexpr int value = 1; };

// ok on gcc, fires on clang which thinks foo<42L>::value is 1
static_assert(foo<42L>::value == 0);

Это лягушка. 42L явно соответствует auto, никаких вопросов нет. Но соответствует ли он int I? Нет, из [temp.deduct.type]/19:

Если P имеет форму, содержащую <i>, и если тип i отличается от типа соответствующего параметра шаблона, названного с помощью простого ID-шаблона-шаблона, вывод не выполняется. Если P имеет форму, содержащую [i], и если тип i не является целочисленным, вывод не выполняется. [Пример:

template<int i> class A { /* ... */ };
template<short s> void f(A<s>);
void k1() {
  A<1> a;
  f(a);             // error: deduction fails for conversion from int to short
  f<1>(a);          // OK
}


template<const short cs> class B { };
template<short s> void g(B<s>);
void k2() {
  B<1> b;
  g(b);             // OK: cv-qualifiers are ignored on template parameter types
}

— end example ]

Чтобы увидеть, соответствует ли 42L специализации, нам нужно вывести int I из 42L, и это не удалось. Следовательно, мы придерживаемся основной специализации. Clang не делает этого. Подано 43076.