Is substitution failure an error with dependent no

2019-03-25 14:34发布

问题:

Let's say I have these template aliases:

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;

I can do the following in 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 {});
}

It prints:

is polymorphic
is not polymorphic

Which matches my expectations.

With clang that code does not compile. It produces the following error messages.

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.

Should it compile? Which of the two compilers is faulty?

回答1:

First and foremost, thanks to @Richard Smith on the #llvm IRC Channel on oftc for the explanation.
Unfortunately, this is not legal C++ and as such Clang is correct: {} is not an expression but a braced-init-list and as such will never be a constant expression as is needed in the initializer of a non-type template parameter.

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

A template-argument for a non-type, non-template template-parameter shall be one of:

  • for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
  • [...]

One solution would be a dummy value in enabler.