If I understand the rules for the lifetime of temporaries correctly, this code should be safe since the lifetime of the temporary stringstream
in make_string()
lasts until the end of the complete expression. I'm not 100% confident there's not a subtle problem here though, can anyone confirm if this usage pattern is safe? It appears to work fine in clang and gcc.
#include <iomanip>
#include <iostream>
#include <sstream>
using namespace std;
ostringstream& make_string_impl(ostringstream&& s) { return s; }
template<typename T, typename... Ts>
ostringstream& make_string_impl(ostringstream&& s, T&& t, Ts&&... ts) {
s << t;
return make_string_impl(std::move(s), std::forward<Ts>(ts)...);
}
template<typename... Ts>
string make_string(Ts&&... ts) {
return make_string_impl(ostringstream{}, std::forward<Ts>(ts)...).str();
}
int main() {
cout << make_string("Hello, ", 5, " World!", '\n', 10.0, "\n0x", hex, 15, "\n");
}
C/C++ use lexical scoping. The lifetime of a variable or object is the time period in which the variable/object has valid memory. Lifetime is also called "allocation method" or "storage duration."
The lifetime of a variable is the time during which the variable stays in memory and is therefore accessible during program execution. The variables that are local to a method are created the moment the method is activated (exactly as formal parameters) and are destroyed when the activation of the method terminates.
The lifetime of a temporary object may be extended by binding to a const lvalue reference or to an rvalue reference (since C++11), see reference initialization for details.
The lifetime of an object begins when its initialization is complete, and ends when its storage is released. Dynamic storage duration starts when the storage created by (new Type) is initialized, and ends when the object goes out of scope or is deleted by “delete pointer”.
The relevant part of the standard is in §12.2
:
12.2.3) Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created.
Except:
12.2.4) There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression. The first context is when a default construct is called to initialize an element of an array. ... [doesn't apply]
12.2.5) The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
...
A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full-expression containing the call.
So there you go. The temporary stringstream{}
is bound to a reference in a function call, so it persists until the completion of the expression. This is safe.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With