为何交换printElement重载顺序后,嵌套vector编译才正常?
问题描述
我的代码如下:
#include <type_traits> #include <iostream> template<typename T, typename = void> struct is_container : std::false_type {}; template<typename T> struct is_container <T, std::void_t<decltype(std::begin(std::declval<T>())), decltype(std::end(std::declval<T>()))>> : std::true_type {}; template<typename T, std::size_t N> struct is_container<T[N]>:std::true_type {}; // fun1 template<typename T> typename std::enable_if<is_container<T>::value>::type printElement(const T& v) { for (const auto& i : v) { printElement(i); } std::cout << '\n'; } // fun2 template<typename T> typename std::enable_if<!is_container<T>::value>::type printElement(const T& v) { std::cout << v << ' '<<'\t'; }
当我用这段代码输出嵌套vector时,编译失败,错误信息为:
C2672 "printElement": 找不到匹配的重载函数
有趣的是,若交换fun1和fun2的位置,代码就能正常运行!请问这是为什么?
原因分析
这是C++模板重载决议与模板声明可见性共同作用的结果:
- 模板可见性规则:C++编译器解析函数模板内部的调用时,只会考虑当前已经声明/定义完成的模板,后续定义的模板不会参与本次重载决议。
- 原代码的触发逻辑:当调用
printElement处理嵌套vector时,外层会匹配fun1(容器版本)并实例化。在fun1的循环中调用printElement(i)时,编译器此时只能看到fun1的声明(fun2还在后面未定义)。对于vector中的非容器元素(比如int),is_container<int>::value为false,导致fun1的enable_if条件不满足,编译器找不到任何匹配的重载,因此抛出C2672错误。 - 交换位置后的正常逻辑:把fun2放在fun1前面后,编译器解析fun1内部的
printElement(i)调用时,已经能看到fun2的声明。对于非容器类型的元素,fun2的enable_if条件满足,就能匹配到正确的重载,编译顺利通过。
如果不想依赖函数定义顺序,也可以改用C++17及以后支持的constexpr if写法,避免重载顺序问题:
template<typename T> void printElement(const T& v) { if constexpr (is_container<T>::value) { for (const auto& i : v) { printElement(i); } std::cout << '\n'; } else { std::cout << v << ' ' << '\t'; } }
内容的提问来源于stack exchange,提问作者softdream
相关产品推荐
相关产品推荐

