我正在尝试禁用具有非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"));
}

但是编译失败



并显示以下错误消息:



这意味着SFINAE的两个条件都成功了,并且两个ctor都被使用了。

我继续尝试以下方法:
#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"));
}

幸运的是,它可以编译并正常工作(live example)。

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

最佳答案

这些条件都不都是真的,那是不可能的。您可以通过第二种方法起作用的事实来确保这一点,如果两种方法都仍然正确,则不会发生。

要记住的重要一点是,默认模板参数不是功能模板签名的一部分。如果我们稍微减少两个 Controller 的签名,我们将得到:

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

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

两者是相同的。因此,发生的是U的模板参数推导,并尝试进行替换以查看选择哪个重载。即使我们无法完成其中一个重载的模板参数推导(在其中一个重载中最后一个参数始终不予说明),但是在尝试推论过程时,我们仍然会找到两个具有相同签名的模板。因此程序变得格式错误。

第二种方法之所以有效,是因为签名本身取决于要评估的enable_if。这就是为什么两个重载之一总是被静默删除的原因,好像它从来没有出现过一样。

10-02 01:06
查看更多