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

Posted

技术标签:

【中文标题】具有模板化类成员函数的 SFINAE【英文标题】:SFINAE with Templated class Member functions 【发布时间】:2018-12-11 04:57:21 【问题描述】:

我正在尝试了解 SFINAE,但在尝试将其应用于模板类成员函数时遇到了问题。感谢Why doesn't SFINAE (enable_if) work for member functions of a class template?,我能够设置一个基本示例(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,...),这可能是巨大的。是否有更短/更清洁的解决方案?

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

【问题讨论】:

【参考方案1】:

out of line 定义中,您删除了默认参数,它们取自声明:

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;

其他人也一样

包罗万象的默认情况(打印 sfinae-default)目前必须写为not(type1, type2,...),这可能是巨大的。是否有更短/更清洁的解决方案?

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

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(...),即如果他们是 SFINAE。

【讨论】:

谢谢,我可以确认这是有效的!也就是说:(1)没有额外的“int”参数是不可能的吗? (2) 你能否在你的答案中添加一些关于排名如何运作的细节?

以上是关于具有模板化类成员函数的 SFINAE的主要内容,如果未能解决你的问题,请参考以下文章

具有指向成员函数的指针的 SFINAE 类型特征失败

成员模板函数的访问控制

SFINAE模板类复杂类型的静态成员定义

C函数中如何调用未实例化类的成员函数

C++ Primer 5th笔记(chap 16 模板和泛型编程)类模板成员函数的实例化

类模板构造函数中的 SFINAE