具有函数和模拟部分模板专业化的 SFINAE

SFINAE with functions and emulating partial template specialization

我正在尝试创建一个函数 X,它在提供成员函数 Y 时专门化,如果没有提供成员函数 Y,则函数 X 使用全局非成员函数 Y 来实现相同的效果。

我目前正在尝试使用以下代码实现此目的

template <typename Container, typename std::enable_if_t<std::is_same<
            decltype(std::declval<Container>().y()),
            decltype(std::declval<Container>().y())>::value>* = nullptr>
void do_something(Container&& container) {
    return std::forward<Container>().y();
}

template <typename Container, typename std::enable_if_t<!std::is_same<
            decltype(std::declval<Container>().y()),
            decltype(std::declval<Container>().y())>::value>* = nullptr>
void do_something(Container&& container) {
    return y(std::forward<Container>(container);
}

但在容器同时具有成员函数 y 和全局非成员函数 y 的情况下,它也适用。存在编译错误,因为对于函数的第二个版本,模板参数格式不正确,因为需要成员函数 y。

知道如何解决这个问题吗?

注意:这不同于仅仅检测class是否具有给定签名的函数。对不起,如果这个问题似乎是这样问的!下面接受的答案应该使我的意图更清楚。

典型的方法是分派到一对函数,如果满足您的条件,则首选一个,而另一个只是后备。好处是你甚至不需要 enable_if,只需要尾随的 decltype:

template <class C>
auto do_something_impl(C&& c, int)
    -> decltype(std::forward<C>(c).y())
{
    return std::forward<C>(c).y();
}

template <class C>
auto do_something_impl(C&& c, ...)
    -> decltype(y(std::forward<C>(c))
{
    return y(std::forward<C>(c));
}

现在只需传入 0:

template <class C>
auto do_something(C&& c)
    -> decltype(do_something_impl(std::forward<C>(c), 0))
{
    return do_something_impl(std::forward<C>(c), 0);
}

int 的转换顺序比到 ... 的转换顺序要好,因此如果该类型具有您想要的成员函数,那么重载将是首选,即使两者都是可行的候选者。