#include <iostream>
#include <type_traits>
// 辅助模板,用于检测类型T是否有size()成员函数
template <typename T, typename = void>
struct has_size_member : std::false_type {};
template <typename T>
struct has_size_member<T, std::void_t<decltype(std::declval<T>().size())>> : std::true_type {};
// 主函数模板,仅当T有size()成员函数时实例化
template <typename T, std::enable_if_t<has_size_member<T>::value, int> = 0>
auto get_size(T&& t) -> decltype(t.size()) {
return t.size();
}
int main() {
std::string s = "hello";
std::cout << "Size of string: " << get_size(s) << std::endl;
// 下面这行如果取消注释会编译错误,因为int没有size()成员函数
// int num = 10;
// std::cout << "Size of int: " << get_size(num) << std::endl;
return 0;
}
SFINAE作用机制解释
has_size_member
模板:
- 首先定义了偏特化版本
has_size_member<T, void>
,默认情况下它继承自std::false_type
,意味着在没有其他特化的情况下,假设类型T
没有size()
成员函数。
- 然后定义了更具体的特化版本
has_size_member<T, std::void_t<decltype(std::declval<T>().size())>>
。这里使用了std::void_t
来检查decltype(std::declval<T>().size())
是否是一个有效的表达式。如果T
类型的对象可以调用size()
函数,那么decltype(std::declval<T>().size())
是有效的,这个特化版本会被匹配,从而继承自std::true_type
。
get_size
函数模板:
std::enable_if_t<has_size_member<T>::value, int> = 0
是std::enable_if
的使用。它作为函数模板的一个额外模板参数,只有当has_size_member<T>::value
为true
时(即T
有size()
成员函数),整个函数模板才是有效的。如果has_size_member<T>::value
为false
,那么这个函数模板会在重载决议阶段被忽略,而不会导致编译错误,这就是SFINAE技术“替换失败不是错误”的体现。这样就实现了仅当类型T
支持size()
成员函数时,get_size
函数模板才会被实例化。