为什么这个SFINAE失败了?

时间:2020-03-06 08:37:00

标签: c++ sfinae

struct BLA
{

};


template<typename T>
class DUMMY
{
public:
    DUMMY() = default;

    template<typename U = T, typename = void>
    void someFunction()
    {
        std::cout << std::is_same<U, BLA>::value << "\n";
        std::cout << "someFunction() - DEFAULT\n";
    }

    template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
    void someFunction()
    {
        std::cout << "someFunction()\n";
    }
};


int main()
{
    DUMMY<BLA> dummy;

    dummy.someFunction();
}

为什么此SFINAE代码调用显示“ someFunction()-DEFAULT”的someFunction()?它应该调用另一个。显然,std :: is_same :: value是真实的。

2 个答案:

答案 0 :(得分:2)

template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type> 将导致template<typename U = T, void>中(正确替换)无效。

您可能会更改为

template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, int>::type = 0>

但是随后,这两个功能都是可行的,而且模棱两可。

所以,您最终可能会做

    template<typename U = T, typename std::enable_if<!std::is_same<U, BLA>::value, int>::type = 0>
    void someFunction()
    {
        std::cout << std::is_same<U, BLA>::value << "\n";
        std::cout << "someFunction() - DEFAULT\n";
    }

    template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, int>::type = 0>
    void someFunction()
    {
        std::cout << "someFunction()\n";
    }

在C ++ 17中,操作更简单

    void someFunction()
    {
        if constexpr (std::is_same<U, BLA>::value) {
            std::cout << "someFunction()\n";
        } else {
            std::cout << std::is_same<U, BLA>::value << "\n";
            std::cout << "someFunction() - DEFAULT\n";
        }
    }

答案 1 :(得分:0)

我正在写新答案,因为它不适合发表评论。除了@ Jarod42。

您似乎以为

template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>

替换为

template<typename U = T, typename = void>

但不是。替代

template<typename U = T, void>

因此,您应该将其声明为

template<typename U = T, typename = typename std::enable_if<std::is_same<U, BLA>::value, void>::type>

因为您使用的typename用于指定类型是从属类型,而不是用于模板参数声明。但是无论如何,它们都不起作用。一个会静默删除格式错误的文件,另一个会导致对同一函数的多次声明。

在您发表评论后,我会尝试解释更详细。

template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
void someFunction()
{
    std::cout << "someFunction()\n";
}

如果T == BLA,则U变成BLA,并得到std::is_same< U , BLA>::value true。所以结果看起来像这样

template<typename U = BLA, void>

如果T == NotBlaType,则U变成NotBlaType,并得到std::is_same<U,BLA>::value false。结果是替换失败,std::enable_if<false,void>没有type

但在两种情况下均未声明该函数。因为不能将void用作non-type template parameter

但是,如果我们将void更改为int,则可以使用。这就是@ Jarod42建议int的原因。

template<void>不合法。

但是template<int = 2>是合法的。

使声明有效后,应有条件地切换函数的声明(因为两个函数具有相同的签名,因此会导致多个声明)。这就是@ Jarod42答案中的两个函数都具有std::enable_if来评估彼此求反的原因。

相关问题