Search code examples
c++vectorlanguage-lawyerundefined-behaviordouble-free

What wording makes this double-free-causing call `std::vector<T>::clear()` in `T`'s constructor undefined behavior?


Consider the following code:

#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
}

Here I call v.emplace_back which calls Foo(int) which calls v.clear() before emplace_back is completed. Seemingly, std::vector is unprepared for that mess and my implementation (Ubuntu 22.04's libstdc++) results in double free: v[0] is first destroyed by clear(), and later by v's destructor.

What wording in any C++ standard makes the behavior of the program above undefined? I'm "simply" calling some operations on v. I don't even access any elements of v.


Solution

  • There is an active LWG issue 2414 to clarify reentrancy when different member functions of library objects are called. The current wording in [reentrancy] only specifies that it is implementation-defined which library functions are reentrant with regards to themselves (unless overruled by more specific rules).