SFINAE技术

1、SFINAE + 模板特化 用于判断类型 T 是否定义了非 voidvalue_type 成员类型

1、默认模板定义 无 value_type 或为 void

  • 模板结构体 has_non_void_value_type默认版本

  • 第二个模板参数是 void,这个参数之后用 SFINAE 替换。

  • 默认情况下,这个结构体继承自 std::false_type,表示 "没有 value_type 或它是 void"。

template<typename T, typename = void>
struct has_non_void_value_type : std::false_type {};

2、SFINAE 检查是否有非-void 的 value_type

  • 这是 SFINAE(Substitution Failure Is Not An Error)机制:
    • 如果 T::value_type 不存在 → 替换失败 → 回退到默认模板
    • 如果存在但是 void → 也不满足 !is_void → 替换失败 → 仍然用默认模板
    • 只有存在且非 void → 替换成功 → 使用这个特化(true_type
template <typename T>
struct has_non_void_value_type<T, std::enable_if_t<!std::is_void_v<typename T::value_type>>> : std::true_type {};

3、根据是否有 value_type 来分支行为

定义一个模板 TypePrinter,有两个模板参数:

  • T:类型
  • HasValueType:布尔值,默认为 has_non_void_value_type<T>::value,即我们上面判断的结果。

没有定义这个主模板的具体实现(仅声明),后面两个特化版本来实现。

template<typename T, bool HasValueType = has_non_void_value_type<T>::value>
struct TypePrinter;

4、特化版本 1(有 value_type

  • 如果 T 有一个非 void 的 value_type,这个版本会被实例化并调用。

  • print() 会输出对应提示信息。

template<typename T>
struct TypePrinter<T, true> {
    static void print(){
        std::cout << "T has a member type called value_type" << std::endl;
    }
};

5、特化版本 2(无 value_type

  • 如果 T 没有 value_type,或者它是 void,就会使用这个版本。
  • print() 会输出另一个提示信息。
template<typename T>
struct TypePrinter<T, false> {
    static void print(){
        std::cout << "T does not have a member type called value_type" << std::endl;
    }
};

测试用的结构体

struct WithValueType {
    using value_type = int; // 有 value_type,类型为 int
};

struct WithoutValueType {
    // 没有 value_type 成员类型
};

完整代码

// 默认模板:用于判断类型 T 是否具有非 void 的成员类型 value_type
// 如果没有,则继承自 std::false_type(表示为 false)
template<typename T, typename = void>
struct has_non_void_value_type : std::false_type{};

// 特化版本:当 T 拥有成员类型 value_type,并且它不是 void 类型时
// std::enable_if_t<...> 会启用这个模板特化(否则 SFINAE 会使这行无效)
// 此时继承 std::true_type,表示 T 有非 void 的 value_type 成员类型
template <typename T>
struct has_non_void_value_type<T, std::enable_if_t<!std::is_void_v<typename T::value_type>>> : std::true_type{};

// 根据上面的判断结果,定义一个辅助类 TypePrinter
// HasValueType 默认为 has_non_void_value_type<T>::value 的结果(true 或 false)
template<typename T, bool HasValueType = has_non_void_value_type<T>::value>
struct TypePrinter;

// 当 HasValueType 为 true,即 T 有非 void 的 value_type 成员类型
// 这个模板特化会被实例化
template<typename T>
struct TypePrinter<T, true>{
    static void print(){
        std::cout <<"T has a member type called value_type" << std::endl;
    }
};

// 当 HasValueType 为 false,即 T 没有 value_type,或者 value_type 是 void
// 这个模板特化会被实例化
template<typename T>
struct TypePrinter<T, false>{
    static void print(){
        std::cout << "T does not have a member type called value_type" << std::endl;
    }
};

// 定义一个测试结构体,有 value_type 成员类型,类型为 int
struct WithValueType{
    using value_type = int;
};
// 定义另一个测试结构体,没有 value_type 成员类型
struct WithoutValueType{};

使用示例:

int main() {
    TypePrinter<WithValueType>::print();     // 输出: T has a member type called value_type
    TypePrinter<WithoutValueType>::print();  // 输出: T does not have a member type called value_type

2、SFINAE 技术 + 成员函数指针判断 ,检查一个类是否有名为 foo() 的成员函数,并据此选择性调用它

主体结构:has_foo<T>

//定义一个模板类 has_foo<T>,用于检测类型 T 是否拥有一个名为 foo() 的成员函数,返回值是 bool 类型的静态常量 value。
template<typename T>
class has_foo{
private:
    // 内部定义两个不同大小的类型:用于区分匹配与不匹配的情况
    typedef char yes[1];
    typedef char no[2];
    // 模板结构体,第二个参数是一个指向类成员函数的指针
    template<typename U, void(U::*)()>
    struct SFINAE{};
	
    // 检测函数重载
    // 如果 U 拥有 void foo() 这个成员函数,那么 &U::foo 是合法的,能成功替换 SFINAE<U, &U::foo>。
    template<typename U>
    static yes & test(SFINAE<U, &U::foo>*);
	
    // 如果 &U::foo 替换失败,就会匹配这个可变参数版本,返回类型为 no&
    template<typename U>
    static no & test(...);

public:
    // 判断最终返回类型:使用 sizeof(...) 来区别
    static constexpr bool value = sizeof(test<T>(0)) == sizeof(yes);
};

call_foo 函数:SFINAE 控制函数是否生成

  • 如果 has_foo<T>::value == truestd::enable_if<..., void>::type 会等价于 void

  • 如果 has_foo<T>::value == falseenable_if 替换失败,整个函数模板会从重载集中移除

template<typename T>
typename std::enable_if<has_foo<T>::value, void>::type
call_foo(T & obj){
    obj.foo();
    std::cout << "foo() called" << std::endl;
}

测试用例:

  • WithFoo 有一个成员函数 foo()has_foo<WithFoo>::value == true
  • WithoutFoo 没有 foo()has_foo<WithoutFoo>::value == false
  • 只有当 Tfoo() 函数时,call_foo(obj) 才有效,否则编译失败。
class WithFoo{
public:
        void foo(){std::cout << "WithFoo::foo() called" << std::endl;}
};

class WithoutFoo{};

完整示例


template<typename T>
class has_foo{
private:
    typedef char yes[1];
    typedef char no[2];
    template<typename U, void(U::*)()>
    struct SFINAE{};

    template<typename U>
    static yes & test(SFINAE<U, &U::foo>*);

    template<typename U>
    static no & test(...);

public:
    static constexpr bool value = sizeof(test<T>(0)) == sizeof(yes);
};


template<typename T>
typename std::enable_if<has_foo<T>::value, void>::type
call_foo(T & obj){
    obj.foo();
    std::cout << "foo() called" << std::endl;
}

class WithFoo{
public:
        void foo(){std::cout << "WithFoo::foo() called" << std::endl;}
};

class WithoutFoo{};
int main() {
    WithFoo wf;
    call_foo(wf); // ✅ 编译通过,输出 WithFoo::foo() called 和 foo() called

    WithoutFoo wo;
    call_foo(wo); // ❌ 编译错误,因为 WithoutFoo 没有 foo()
}
posted @ 2025-08-07 15:16  xiaoluosibky  阅读(9)  评论(0)    收藏  举报