C++ transform的lambda返回smatch正则匹配结果错误问题排查
C++ regex匹配结果异常问题原因及解决方案
错误原因
1. 核心问题:smatch不存储字符串副本,仅保留指向原字符串的迭代器
std::smatch本质是std::match_results<std::string::const_iterator>的别名,所有匹配结果都是通过迭代器指向原匹配字符串的对应位置,不会额外拷贝存储字符串内容。- 你的lambda入参写为
auto s,属于值传递,每次调用都会生成一个临时的std::string对象,lambda执行结束后这个临时对象就会被销毁,此时返回的smatch里保存的所有迭代器全部悬空,属于未定义行为。 - 你看到的输出异常是栈内存复用导致的:第一个临时字符串
"a b"销毁后,第二个临时字符串"cd ef"刚好复用了同一块栈内存,悬空的迭代器刚好指向了新的字符串内容,才会出现两次输出都和第二个字符串相关的情况。未定义行为不保证触发运行时报错,出现任何结果都符合预期。 - 补充:原代码中
transform(begin(v), end(), ...)第二个参数end()没有传入容器参数,属于语法错误,实际可运行的代码应该是end(v)。
2. *new smatch的写法存在内存泄漏
你在堆上申请了smatch对象,解引用后拷贝返回,堆上的原始对象没有被释放,直接造成内存泄漏。就算你换成栈上声明的smatch m{},也解决不了迭代器悬空的核心问题,所以输出结果还是错误的。
3. 第二段代码编译失败的原因
你声明的容器是vector<smatch> P,存储的是smatch对象,但lambda里返回的是new smatch得到的smatch*指针,类型不匹配,所以编译直接报错。如果要存指针需要把容器声明改为vector<smatch*> P,但就算改了也还是存在迭代器悬空的问题,运行结果依然错误。
修复方案
两种可选方案:
方案1:修改lambda入参为常量引用,避免生成临时字符串
该方案需要保证原输入vector的生命周期长于存储smatch的容器,否则仍会出现迭代器悬空:
#include <regex> #include <vector> #include <string> #include <algorithm> #include <iostream> using namespace std; void f(const vector<string>& v) { vector<smatch> P{}; // 正则对象提前构造,避免每次lambda调用重复生成 static const regex r{"(\\S*) (\\S*) ?(.*)"}; // 入参改为const string& s,直接引用原vector里的字符串,不生成临时对象 transform(begin(v), end(v), back_inserter(P), [](const string& s){ smatch m; regex_match(s, m, r); return m; }); for (const auto& s: P) cout << s[0] << endl; }
方案2:直接存储匹配结果的字符串副本,不依赖原对象生命周期
如果无法保证原输入vector的生命周期,直接把匹配结果转成string存储更安全:
#include <regex> #include <vector> #include <string> #include <algorithm> #include <iostream> using namespace std; void f(const vector<string>& v) { vector<string> P{}; static const regex r{"(\\S*) (\\S*) ?(.*)"}; transform(begin(v), end(v), back_inserter(P), [](const string& s){ smatch m; regex_match(s, m, r); // 直接返回字符串副本,后续使用不依赖原字符串和smatch的生命周期 return m.str(0); }); for (const auto& s: P) cout << s << endl; }
内容的提问来源于stack exchange,提问作者ByteEater
相关产品推荐
相关产品推荐

