SFINAE检测size方法失效:嵌套vector无法被正确识别
问题描述
我期望这段代码能够正确检测嵌套vector的size方法,但目前仅能识别外层vector的size方法,内层嵌套的vector均未被检测到,恳请各位提供帮助。
原代码
#include <iostream> #include <vector> // SFINAE test template <typename T> class has_size { typedef char one; struct two { char x[2]; }; template <typename C> static one test( decltype(&C::size) ) ; template <typename C> static two test(...); public: enum { value = sizeof(test<T>(0)) == sizeof(char) }; }; int main(int argc, char *argv[]) { float x=2; std::cout << typeid(x).name() << '\n'; std::cout << has_size<decltype(x)>::value << std::endl; std::vector<std::vector<std::vector<float>>> y{}; std::cout << typeid(y).name() << '\n'; std::cout << has_size<decltype(y)>::value << std::endl; std::cout << typeid(y.at(0)).name() << '\n'; std::cout << has_size<decltype(y.at(0))>::value << std::endl; std::cout << typeid(y.at(0).at(0)).name() << '\n'; std::cout << has_size<decltype(y.at(0).at(0))>::value << std::endl; return 0; }
原输出结果
f 0 St6vectorIS_IS_IfSaIfEESaIS1_EESaIS3_EE 1 St6vectorIS_IfSaIfEESaIS1_EE 0 St6vectorIfSaIfEE 0
问题分析与修复
问题根源
- 引用类型导致检测失败:
y.at(0)和y.at(0).at(0)的类型是引用类型(std::vector<...>&),当前的has_size模板直接用引用类型去获取&C::size时,SFINAE会触发失败——因为引用类型本身没有成员,成员属于被引用的底层容器类型。 - 运行时异常风险:空vector调用
at(0)会抛出std::out_of_range异常,虽然decltype是编译期操作不影响类型推导,但实际运行程序会崩溃(你的输出能正常打印可能是编译器未严格检查,但这是未定义行为)。
修复后的代码
#include <iostream> #include <vector> #include <type_traits> // 引入remove_reference_t // SFINAE test - 修复版本 template <typename T> class has_size { typedef char one; struct two { char x[2]; }; // 改用std::declval检测size是否可调用,同时先去除引用类型 template <typename C> static one test(decltype(std::declval<std::remove_reference_t<C>>().size())*); template <typename C> static two test(...); public: enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; }; int main(int argc, char *argv[]) { float x=2; std::cout << typeid(x).name() << '\n'; std::cout << has_size<decltype(x)>::value << std::endl; std::vector<std::vector<std::vector<float>>> y{}; std::cout << typeid(y).name() << '\n'; std::cout << has_size<decltype(y)>::value << std::endl; // 改用decltype(y[0])替代y.at(0),避免运行时异常(decltype是编译期操作,不会实际执行y[0]) using InnerVec1 = decltype(y[0]); std::cout << typeid(InnerVec1).name() << '\n'; std::cout << has_size<InnerVec1>::value << std::endl; using InnerVec2 = decltype(y[0][0]); std::cout << typeid(InnerVec2).name() << '\n'; std::cout << has_size<InnerVec2>::value << std::endl; return 0; }
修复说明
- 处理引用类型:用
std::remove_reference_t<C>先剥离类型的引用属性,确保检测的是底层容器类型的size成员。 - 更可靠的检测方式:通过
std::declval<C>().size()来检测是否存在可调用的size成员,这种方式支持更多场景(比如const类型、引用类型),比直接取成员函数指针更灵活。 - 避免运行时异常:用
decltype(y[0])替代decltype(y.at(0)),因为y[0]是编译期类型推导,不会实际执行越界访问,避免运行时抛出异常。
修复后输出
f 0 St6vectorIS_IS_IfSaIfEESaIS1_EESaIS3_EE 1 St6vectorIS_IfSaIfEESaIS1_EE& 1 St6vectorIfSaIfEE& 1
内容的提问来源于stack exchange,提问作者Fzza
相关产品推荐
相关产品推荐

