10

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?

Xeo
  • 129,499
  • 52
  • 291
  • 397
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • Ooops, I feel silly. I have a feeling this has nothing to do with template aliases, so the title is possibly misleading :S Sorry about that, I'll investigate a bit and fix the title if that turns out the case. – R. Martinho Fernandes Apr 16 '12 at 19:48
  • 2
    `DisableIf> = {}` is that legal initializer list initialization? Can structures be template value parameters? – jpalecek Apr 16 '12 at 19:49
  • @jpalecek No, structures can't. That's why I use an enum :) – R. Martinho Fernandes Apr 16 '12 at 19:51
  • Clang issues similar error messages if I don't use the aliases and just manually "inline" them, so I fixed the title. – R. Martinho Fernandes Apr 16 '12 at 19:55
  • Quickly scanning the standard, it seems to be legal, so it's probably an issue with clang. Does it still fail on clang if you change `enabler` to `int` and `{}` to `0`? – jpalecek Apr 16 '12 at 20:20
  • BTW, the standard says you should have at least one value in your enum. – jpalecek Apr 16 '12 at 20:32
  • Out of curiosity, why do you want to pass an enum as the second argument to the template? Why not `template >::type > void f( T );` ? – David Rodríguez - dribeas Apr 16 '12 at 20:35
  • @jpalecek No, it doesn't say that. Check the grammar at §7.2, paragraph 1: the enumerator list is optional. Check also paragraph 6 for an explicit mention of an empty enumerator list. – R. Martinho Fernandes Apr 16 '12 at 20:36
  • @DavidRodríguez-dribeas try it out on this code and you'll see. You'll end up redeclaring the *same* template with different default arguments, which is not allowed. More info on the thought process that led to this style of enable_if: http://loungecpp.wikidot.com/tips-and-tricks:enable-if-for-c-11 – R. Martinho Fernandes Apr 16 '12 at 20:38
  • @R.MartinhoFernandes: Oh yes, it does. Read 7.2 par. 2: The optional identifier shall not be omitted in declaration of a scoped enumeration. – jpalecek Apr 16 '12 at 22:05
  • 1
    @jpalecek That's for the enum's *name*. It refers to the fact that `enum {}` is valid, but `enum class {}` isn't. Not at all relevant here. – R. Martinho Fernandes Apr 16 '12 at 22:06

1 Answers1

7

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.

Community
  • 1
  • 1
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • 2
    That is not a good argument - we are not talking about template arguments here (noone tries eg. `f`), but default arguments, which have a syntax of a parameter-declaration, which can, in principle, have `{}` on the rhs (and if `enabler{}` is a constant expression, or `x`, given `enabler x{}`, there shouldn't be a problem with constant). However, 8.3.6/3 says there should be an expression in case of template parameter declaration. – jpalecek Apr 16 '12 at 22:40
  • @jpalecek: "oone tries eg. `f`" Uh, calling `f` does *exactly* that. – GManNickG Apr 16 '12 at 23:52
  • 1
    @GManNickG: No, it doesn't. Default parameters are not specified (at least explicitly) as text substitutions. – jpalecek Apr 18 '12 at 15:35
  • @jpalecek: In what way does a default argument differ from a formal argument? In any case you can't just use `{}` because it can never be a constant expression, which is what's needed to initialize a non-type parameter. – Xeo Apr 18 '12 at 15:55
  • 1
    @jpalecek Is paragraph 9 of 14.1 Template Parameters "A *default template-argument* is a *template-argument* (14.3) specified after `=` in a *template-parameter*." the missing argument in this case? – Luc Danton Apr 22 '12 at 04:15