Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SFINAE on constructors works in VC2017 but not in clang/gcc [duplicate]

Tags:

c++

c++14

#include <type_traits>

template<bool b>
struct S
{
    template<typename = std::enable_if_t<b>>
        S() {}
    template<typename = std::enable_if_t<!b>>
        S(int) {}
};

S<true> s{}; // error in clang/gcc, OK in VC2017
S<false> s{0}; // error in clang/gcc, OK in VC2017

In both cases clang/gcc try to instantiate the ctor that should actually be discarded due to SFINAE. The error message is:

error : no type named 'type' in 'std::enable_if< false, void>'; 'enable_if' cannot be used to disable this declaration

clang/gcc's instantiation of the other ctor is incorrect since it should not be on the list of possible overloads, right?

But before I file a bug I would like to read what others think. Maybe I don't get it right...

like image 767
x y Avatar asked Oct 06 '17 10:10

x y


2 Answers

This is a bug in MSVC; clang and gcc are right.

The problem is that SFINAE happens during overload resolution only, not before. What I mean is, if the function is ill-formed even before you call it, it's an error.

When you use S<true> for example, the whole class is instantiated. It will look a bit like this:

struct S_true
{
    template<typename = void>
    S() {}

    template<typename = /*fail*/>
    S(int) {}
};

As you can see, the second constructor is completely ill-formed, it's not a valid definition, because there is no type type found (because of std::enable_if). So SFINAE cannot even kick in, the class definition is ill-formed and diagnosed.

You need to make the template parameter b part of the template parameter list of both constructors (have a look at @bolov's answer).

like image 100
Rakete1111 Avatar answered Sep 22 '22 09:09

Rakete1111


@Rakete1111 is 100% right.

You need to make the template parameter bool b part of the template parameter list of both constructors.

Here is how to do this (it's a pretty standard technique):

template<bool b>
struct S
{
    template<bool bb = b, typename = std::enable_if_t<bb>>
        S() {}
    template<bool bb = b, typename = std::enable_if_t<!bb>>
        S(int) {}
};
like image 44
bolov Avatar answered Sep 19 '22 09:09

bolov