具有模板化 class 成员函数的 SFINAE

SFINAE with Templated class Member functions

我正在尝试了解 SFINAE,但在尝试将其应用于模板 class 成员函数时遇到了问题。感谢 I was able to setup a basic example(Run online):

template<typename T>
class Foo{
    public:
    template <typename U = T, typename std::enable_if_t<!std::is_same<U, int>::value && !std::is_same<U, float>::value,int> = 0>
    void sfinae() { // Foo<anything else>
        std::cout << "sfinae default" << std::endl;
    }

    template <typename U = T, typename std::enable_if_t<std::is_same<U, int>::value,int> = 0>
    void sfinae() { // Foo<int>
        std::cout << "sfinae int" << std::endl;
    }

    template <typename U = T, typename std::enable_if_t<std::is_same<U, float>::value,int> = 0>
    void sfinae() { // Foo<float>
        std::cout << "sfinae float" << std::endl;
    }
};

我有两个后续问题:
1. 成员函数的声明和定义如何分开?
我尝试了如下定义:

template <typename T>
template <typename U = T, typename std::enable_if_t<!std::is_same<U, int>::value && !std::is_same<U, float>::value,int> = 0>
    void Foo<T>::sfinae() { // Foo<anything else>
        std::cout << "sfinae default" << std::endl;
    }

但是这给出了编译错误。

2. 包罗万象的默认情况(打印 sfinae-default)目前必须写成 not(type1, type2,...),这可能会很大。有没有可能的shorter/cleaner解决方案?

编辑: 根据@Jans 的回答,这里是 corrected solution.

行外定义中,您删除了默认参数,它们取自声明:

template <class T>
template <typename U, typename std::enable_if_t<!std::is_same<U, int>::value && !std::is_same<U, float>::value,int>>
void Foo<T>::sfinae() { // Foo<anything else>
    std::cout << "sfinae default" << std::endl;
}

其他人也一样

The catch-all default case(which prints sfinae-default) has to currently be written as not(type1, type2,...) which can potentially be huge. Is there a shorter/cleaner solution possible?

为此,您需要添加一个额外的参数来对重载进行排名,将最低的排名分配给包罗万象的重载,这通常是使用省略号参数并不比任何其他参数类型更好的事实来完成的:

template <typename U = T>
void sfinae(...);

template <typename U = T, typename std::enable_if_t<std::is_same<U, int>::value,int> = 0>
void sfinae(int);

template <typename U = T, typename std::enable_if_t<std::is_same<U, float>::value,int> = 0>
void sfinae(int);

Foo<char>{}.sfinae(0); // select the catch-all

这意味着由于 ... 比任何其他参数都差(int... 好),所以仅当不能调用其他两个,即如果它们是 SFINAE。