何种C++标准条款判定T构造函数中调用std::vector<T>::clear()的行为未定义?
问题分析:容器操作过程中修改容器导致的未定义行为
考虑以下代码:
#include <string> #include <vector> struct Foo; std::vector<Foo> v; struct Foo { std::string s = std::string(10000, 'x'); Foo() {} Foo(int) { v.clear(); } }; int main() { v.resize(1); // required, otherwise no double free v.reserve(2); // optional v.emplace_back(10); // causes double free }
这段代码中,调用v.emplace_back(10)会触发Foo(int)构造函数,而该构造函数会在emplace_back执行完成前调用v.clear()。在Ubuntu 22.04的libstdc++实现中,这会导致双释放:v[0]先被clear()销毁,后续又被v的析构函数销毁。
对应的C++标准条款
该程序的行为属于未定义行为,对应的C标准条款在容器通用要求部分(以C20为例,条款编号为[container.reqmts.general]/15):
如果一个容器操作会修改容器,那么在该操作执行期间修改容器的行为是未定义的。
更具体地说,emplace_back是一个会修改容器的操作(它会向容器中添加新元素,可能改变容器的size等状态)。在emplace_back的执行过程中——也就是构造新Foo对象的阶段——调用v.clear()直接修改了同一个容器,这违反了容器操作的基本约束,导致行为未定义。
为什么即使没显式访问元素也会出问题
你提到“并未访问v的任何元素”,但实际上emplace_back的执行过程已经涉及容器的内部状态管理:
- 当调用
emplace_back(10)时,容器会先准备好存储新元素的空间(这里已经reserve(2),所以空间足够); - 然后在该空间上构造
Foo对象,此时触发Foo(int)构造函数; - 构造函数中调用
v.clear(),会销毁容器中已有的所有元素(包括v[0]),并将容器的size重置为0; - 当
emplace_back执行完成后,它原本预期要完成新元素的添加,但容器已经被clear()修改,后续容器析构时,会尝试销毁所有“应该存在”的元素,而这些元素已经被clear()销毁过一次,最终导致双释放错误。
这种在容器修改操作的执行过程中嵌套修改容器的行为,完全符合标准中“未定义行为”的判定条件。
内容的提问来源于stack exchange,提问作者yeputons
相关产品推荐
相关产品推荐

