Является ли сбой замещения ошибкой с зависимыми параметрами непигового шаблона?

Скажем, у меня есть эти псевдонимы шаблонов:

enum class enabler {};

template <typename T>
using EnableIf = typename std::enable_if<T::value, enabler>::type;
template <typename T>
using DisableIf = typename std::enable_if<!T::value, enabler>::type;

Я могу сделать следующее в GCC:

#include <iostream>

template <typename T, EnableIf<std::is_polymorphic<T>> = {}>
void f(T) { std::cout << "is polymorphic\n"; }

template <typename T, DisableIf<std::is_polymorphic<T>> = {}>
void f(T) { std::cout << "is not polymorphic\n"; }

struct foo { virtual void g() {} };

int main() {
    f(foo {});
    f(int {});
}

Он печатает:

является полиморфным
не является полиморфным

Что соответствует моим ожиданиям.

С clang этот код не компилируется. Он выдает следующие сообщения об ошибках.

test.cpp:11:58: error: expected expression
template <typename T, EnableIf<std::is_polymorphic<T>> = {}>
                                                         ^
test.cpp:14:59: error: expected expression
template <typename T, DisableIf<std::is_polymorphic<T>> = {}>
                                                          ^
test.cpp:20:3: error: no matching function for call to 'f'
  f(foo {});
  ^
test.cpp:12:6: note: candidate template ignored: couldn't infer template argument ''
void f(T) { std::cout << "is polymorphic\n"; }
     ^
test.cpp:15:6: note: candidate template ignored: couldn't infer template argument ''
void f(T) { std::cout << "is not polymorphic\n"; }
     ^
test.cpp:21:3: error: no matching function for call to 'f'
  f(int {});
  ^
test.cpp:12:6: note: candidate template ignored: couldn't infer template argument ''
void f(T) { std::cout << "is polymorphic\n"; }
     ^
test.cpp:15:6: note: candidate template ignored: couldn't infer template argument ''
void f(T) { std::cout << "is not polymorphic\n"; }
     ^
4 errors generated.

Должен ли он компилироваться? Какой из двух компиляторов неисправен?

Ответ 1

Прежде всего, благодаря @Ричард Смит на # llvm IRC Channel on oftc для объяснения.
К сожалению, это не легальный С++ и, как таковой, Clang корректен: {} не является выражением, а является скопированным-init-списком и как таковой никогда не будет постоянным выражением, которое необходимо в инициализаторе параметра шаблона непигового типа.

§14.3.2 [temp.arg.non-type] p1

Аргумент шаблона для непигового шаблона-шаблона без шаблона должен быть одним из следующих:

  • для нетипового шаблона-параметра интегрального или перечисляемого типа, преобразованного выражения константы (5.19) типа шаблона-параметра; или
  • [...]

Одним из решений было бы фиктивное значение в enabler.