构造函数上的SFINAE在VC2017中工作,但在clang/gcc中不起作用

x y*_*x y 5 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
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,clang/gcc尝试实例化由于SFINAE而实际应该丢弃的ctor.错误消息是:

错误:'std :: enable_if <false,void>'中没有​​名为'type'的类型; 'enable_if'不能用于禁用此声明

clang/gcc对另一个ctor的实例化是不正确的,因为它应该在可能的重载列表中,对吧?

但在我提交错误之前,我想阅读别人的想法.也许我说得不对劲......

Rak*_*111 11

这是MSVC中的一个错误; clang和gcc是对的.

问题是SFINAE仅在过载分辨率期间发生,而不是之前.我的意思是,如果函数在你调用它之前是不正确的,那就是错误.

S<true>例如,当您使用时,将实例化整个类.它看起来有点像这样:

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

    template<typename = /*fail*/>
    S(int) {}
};
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,第二个构造函数是完全错误的,它不是有效的定义,因为没有type找到类型(因为std::enable_if).因此SFINAE甚至无法启动,类定义是错误的并且被诊断出来.

您需要将模板参数b作为两个构造函数的模板参数列表的一部分(请查看@ bolov的答案).


bol*_*lov 6

@ Rakete1111 100%正确.

您需要使模板参数bool b成为两个构造函数的模板参数列表的一部分.

以下是如何做到这一点(这是一种非常标准的技术):

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) {}
};
Run Code Online (Sandbox Code Playgroud)