std::pair模板重载未触发,C++模板函数调用异常求助
排查C++模板重载失效导致无限递归的问题
嘿,我帮你瞅了下这段代码,问题其实出在两个关键的地方,都是C++模板重载里很容易踩的坑:
先重现下你的问题场景:你写了四个重载模板函数,其中针对std::pair的模板#3完全没被调用,反而触发了通用模板#1,导致无限递归刷"body of f"。先贴下你的代码和输出方便对照:
#include <iostream> #include <vector> #include <map> #include <utility> template <typename T> bool f(T& x) // #1 { std::cout << "body of f\n"; return f(x); } template <typename T> bool f(std::vector<T>& v) // #2 { std::cout << "body of f for vectors\n"; return true; } template <typename Key, typename Value> bool f(std::pair<Key,Value>& v) // #3 { std::cout << "body of f for pairs\n"; for(auto& e: v) { f(e.first); } for(auto& e: v) { f(e.second); } return true; } template <typename Key, typename Value> bool f(std::map<Key,Value>& v) // #4 { std::cout << "body of f for maps\n"; for(auto& e: v) { f(e.first); // expecting this call goes to #3 } for(auto& e: v) { f(e.second); } return true; } int main() { std::vector<int> v{1,2}; std::map<std::pair<int,int>,std::vector<int>> m_map = { {{10,20}, {5,6}}, {{11,22}, {7,8}} }; f(m_map); // this call goes to #4 }
控制台输出:
body of f for maps
body of f
body of f
...(无限重复)
问题1:std::map的元素是带const的pair,你的重载模板不匹配
你可能没注意到:std::map的每个元素是std::pair<const Key, Value>,而不是你写的std::pair<Key, Value>!当你遍历m_map时,e.first的类型是const std::pair<int,int>&——这里的const是关键!
你的模板#3只接受std::pair<Key,Value>&(非const引用),但e.first是const引用,编译器找不到匹配的重载,只能退而求其次调用最通用的模板#1。而模板#1里又递归调用f(x),直接触发无限循环。
问题2:std::pair不能用范围for循环遍历
你的模板#3里写了for(auto& e: v)来遍历std::pair,这完全是语法错误!std::pair不是可迭代容器,没有begin()和end()成员函数,这段代码其实根本过不了编译(除非你的编译器有奇怪的兼容行为)。你应该直接调用f(v.first)和f(v.second),而不是用for循环。
修复方案
我把你的代码修改了一下,解决了这两个问题,同时避免了无限递归:
#include <iostream> #include <vector> #include <map> #include <utility> #include <type_traits> // 针对基本类型(非类类型)的重载,终止递归 template <typename T> std::enable_if_t<!std::is_class_v<T>, bool> f(const T& x) { std::cout << "body of f for scalar type: " << x << "\n"; return true; } // vector的重载,支持const引用 template <typename T> bool f(const std::vector<T>& v) { std::cout << "body of f for vectors\n"; for(const auto& elem : v) { f(elem); } return true; } // pair的重载,支持const引用,修复遍历逻辑 template <typename Key, typename Value> bool f(const std::pair<Key,Value>& p) { std::cout << "body of f for pairs\n"; f(p.first); f(p.second); return true; } // map的重载,支持const引用 template <typename Key, typename Value> bool f(const std::map<Key,Value>& m) { std::cout << "body of f for maps\n"; for(const auto& elem : m) { f(elem.first); // 现在会匹配到pair的const版本 f(elem.second); } return true; } int main() { std::map<std::pair<int,int>,std::vector<int>> m_map = { {{10,20}, {5,6}}, {{11,22}, {7,8}} }; f(m_map); }
修复后的输出
运行这段代码会得到正确的调用链:
body of f for maps body of f for pairs body of f for scalar type: 10 body of f for scalar type: 20 body of f for vectors body of f for scalar type: 5 body of f for scalar type: 6 body of f for pairs body of f for scalar type: 11 body of f for scalar type: 22 body of f for vectors body of f for scalar type: 7 body of f for scalar type: 8
关键总结
- 处理
std::map的元素时,一定要记得key是const的,对应的pair也是带const的,模板重载要匹配const引用; - 不要试图用范围for循环遍历非容器类型(比如
std::pair); - 给通用模板加终止条件,避免无限递归——这里用了
std::enable_if和std::is_class_v来限制通用模板只处理基本类型,你也可以给特定类型(比如int)写特化版本。
内容的提问来源于stack exchange,提问作者TechTotie
相关产品推荐
相关产品推荐

