为什么这个 SFINAE 失败了?

Why this SFINAE fails?

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是真的

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";
        }
    }

我正在写新答案,因为它不适合发表评论。除了@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 == BLAU 变为BLA 并使std::is_same< U , BLA>::value true。所以结果看起来像这样

template<typename U = BLA, void>

如果 T == NotBlaTypeU 变为 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 ,它们相互求反。