SFINAE技术
1、SFINAE + 模板特化 用于判断类型 T 是否定义了非 void 的 value_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 == true,std::enable_if<..., void>::type会等价于void -
如果
has_foo<T>::value == false,enable_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 == trueWithoutFoo没有foo()→has_foo<WithoutFoo>::value == false- 只有当
T有foo()函数时,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()
}

浙公网安备 33010602011771号