C++笔记 - SFINAE

514 阅读1分钟

SFINAE的全称是:Substitution Failure Is Not An Error。在进行模板匹配时,可能会出现错误。而当出现错误时,不抛出错误,而是继续去匹配别的模板。这种情况就是SFINAE。下面看一个例子:

#include <iostream>

// 仅仅是为了获取T的类型
template <bool B, typename T = void> struct enable_if {};
template <typename T> struct enable_if<true, T> { using type = T; };

// 判断是否有函数A()
template <typename T> struct has_func_A {
    
    // decltype返回最后一个表达式的结果,即:bool()的结果true
    // test的int参数是为了使用可变参数函数,因为模板函数的优先级大于可变参数函数
    template <typename C>
    static constexpr decltype(std::declval<C>().A(), bool()) test(int) {
        return true;
    }

    // black hole
    template <typename C> static constexpr bool test(...) { return false; }
    
    static constexpr bool value = test<T>(int());
};

// black hole
void O(...) { std::cout << "O" << std::endl; }

// 根据是否存在函数A,来生成不同的重载函数
template <class T>
typename enable_if<has_func_A<T>::value, int>::type A(const T &obj) {
    return obj.A();
}

template <class T>
typename enable_if<!has_func_A<T>::value, void>::type A(const T &obj) {
    return O(obj);
}

class B {};

class C {
    public:
    int A() const {
        std::cout << "C" << std::endl;
        return 0;
    }
};

int main(void) {
    B b;
    C c;
    A<B>(b); // 输出:O
    A<C>(c); // 输出:C
}

上面的例子,通过编译器类型推倒机制,根据不同的条件生成不同的模板函数,实现灵活的函数重载。