C++ SFINAE:is_constructible for const char [] vs std :: string

dru*_*rus 3 c++ templates sfinae

我正在尝试禁用具有非std :: string可构造类型的ctor.我的第一次尝试是这样的:

#include <iostream>

struct A
{
    template <typename U, typename = typename std::enable_if<std::is_constructible<std::string, U>::value>::type>
    A(U&& val)
    {
        std::cout << "A(std::string&& val)" << std::string(std::forward<U>(val)) << std::endl;
    }

    template <typename U, typename = typename std::enable_if<not std::is_constructible<std::string, U>::value>::type>
    A(U&& val)
    {
        std::cout << "A(int&& val)" << val << std::endl;
    }
};

int main()
{
    A a1(1);
    A a2("hello");
    A a3(std::string("hello"));
}
Run Code Online (Sandbox Code Playgroud)

但编译失败了

A a1(1);

带有以下错误消息:

错误C2535:'A :: A(U &&)':已定义或声明的成员函数(实例).

这意味着,SFINAE的两个条件都成功,并且两个ctors都被使用.

我继续前进并尝试了以下方法:

#include <iostream>

struct A
{
    template <typename U>
    A(U&& val, typename std::enable_if<std::is_constructible<std::string, U>::value>::type* = nullptr)
    {
        std::cout << "A(std::string&& val)" << std::string(std::forward<U>(val)) << std::endl;
    }

    template <typename U>
    A(U&& val, typename std::enable_if<not std::is_constructible<std::string, U>::value>::type* = nullptr)
    {
        std::cout << "A(int&& val)" << val << std::endl;
    }
};

int main()
{
    A a1(1);
    A a2("hello");
    A a3(std::string("hello"));
}
Run Code Online (Sandbox Code Playgroud)

幸运的是它编译并且工作正常(实例).

到目前为止,我对第二个解决方案很好,但我真的不明白为什么使用模板化参数启用/禁用ctor的第一种方法不起作用.

Sto*_*ica 5

条件不是真的,那是不可能的.你可以通过第二种方法的工作得到保证,如果两种方法都是正确的话就不会发生这种情况.

重要的是要记住,默认模板参数不是函数模板签名的一部分.如果我们将两个c'tors减少到他们的签名,我们将得到这个:

template <typename U, typename>
A(U&& val)
{
}

template <typename U, typename>
A(U&& val)
{
}
Run Code Online (Sandbox Code Playgroud)

两者是完全相同的.所以会发生什么是模板参数推导U和尝试进行替换以查看要选择的重载.即使我们无法完成其中一个重载的模板参数推断(最后一个参数总是在其中一个中没有计算),我们仍然会在尝试推导过程时找到两个具有相同签名的模板.所以该计划变得格格不入.

第二种方法有效,因为签名本身取决于enable_if被评估.这就是为什么两个重载中的一个总是会被静默删除的原因,就好像它永远不存在一样.