基于 SFINAE 的重载冲突

SFINAE-based overloads conflict

#include <type_traits>

struct A {};

template <class T, class = std::enable_if_t<std::is_same_v<T, A>>>
void foo(T t) {}

template <class T, class = std::enable_if_t<!std::is_same_v<T, A>>>
void foo(T t) {}

int main()
{
    foo(A{});
    foo(0);
    return 0;
}

我发现 foo() 的第二个定义存在编译错误:

test.cpp:9:6: error: redefinition of ‘template<class T, class> void foo(T)’

看起来这是因为 SFINAE 解析发生在后期阶段。

作为一种解决方法,我可以将另一个模板参数添加到第二个 foo(),方法是将其更改为:

template <class T, class = std::enable_if_t<!std::is_same_v<T, A>>, class = void> void foo(T t) {}

然后错误消失了。但我想知道是否有更惯用的方法来处理这种冲突的重载?对于两个重载,添加一个额外的虚拟模板参数就足够了,但是如果我们有很多这样的重载,那么代码会变得更加混乱,并且这些额外参数的目的从乍一看到 reader.

But I am wondering is there more idiomatic way to handle such conflicting overloads?

举例

template <class T, std::enable_if_t<std::is_same<T, A>{}> * = nullptr>
void foo(T t) {}

template <class T, std::enable_if_t<!std::is_same<T, A>{}> * = nullptr>
void foo(T t) {}

不过我觉得这样更清楚

template <class T>
std::enable_if_t<std::is_same<T, A>{}> foo(T t) {}

template <class T>
std::enable_if_t<!std::is_same<T, A>{}> foo(T t) {}

您还可以添加第二个默认参数。

template <class T>
void foo(T t, std::enable_if_t<std::is_same<T, A>{}> * = nullptr) {}

template <class T>
void foo(T t, std::enable_if_t<!std::is_same<T, A>{}> * = nullptr) {}