Search code examples
c++gccclanglanguage-lawyerenable-if

enable_if's syntactical patterns


I've been using enable_if in this approximate manner with various versions of GCC (up to 5.2):

template< bool b, std::enable_if_t< b >... >
void fn() { std::cout << 1 << std::endl; }
template< bool b, std::enable_if_t< !b >... >
void fn() { std::cout << 2 << std::endl; }
// ...
fn< true >();
fn< false >();

But, as it turns out, Clang 3.7 does not accept this ("call to 'fn' is ambiguous").

Q1. Who's right, and why?

There are, of course, other ways to do it, but I kind of don't like

template< bool b >
std::enable_if_t< b, void > fa() { std::cout << 1 << std::endl; }
// ...

and its ilk for making normal parts of the function signature less readable, and

template< bool b, std::enable_if_t< b, int > = 0 >
void fd() { std::cout << 1 << std::endl; }
// ...

for involving irrelevant elements (types and values).

Q2. What other (correct, more readable, less hackish/weird) ways to use enable_if/enable_if_t are there?


Solution

  • According to the standard 14.1/p7 Template parameters [temp.param] (Emphasis Mine):

    A non-type template-parameter shall not be declared to have floating point, class, or void type.

    Consequently, your code snippet is ill-formed. Thus, GCC is wrong on this.

    However if you change to:

    template< bool b, std::enable_if_t< b, int>... >
    void fn() { std::cout << 1 << std::endl; }
    template< bool b, std::enable_if_t< !b, int>... >
    void fn() { std::cout << 2 << std::endl; }
    

    Restriction is lifted, and this code is legitimate and should be accepted. Apparently, it seems that Clang rejects this code as well. IMHO, this is a Clang bug.

    As I found out a similar bug has been reported 23840.

    Now for the practical part, I don't know if this is practical/less hackish/less weird but you could do the following:

    template< bool b, std::enable_if_t< b, int> = 0 >
    void fn() { std::cout << 1 << std::endl; }
    template< bool b, std::enable_if_t< !b, int> = 0 >
    void fn() { std::cout << 2 << std::endl; }