I read that string literals have static storage duration, does that mean that references to them are always valid? For example, is the following code safe:
std::function<void()> foo(const std::string& name){
// capture name by reference
return [&](){
std::cout<<"The name is "<<name<<std::endl;
};
}
int main() {
std::function<void()> func;
{
func = foo("test");
func(); // func called in the same scope
}
func(); // func called in the outer scope
}
What if instead we first create a string from that string literal, and reference that:
std::function<void()> foo(const std::string& name){
// capture name by reference
return [&](){
std::cout<<"The name is "<<name<<std::endl;
};
}
int main() {
std::function<void()> func;
{
std::string test = "test";
func = foo(test);
func(); // func called in the same scope
}
func(); // func called in the outer scope
}
No, this is not safe.
name
is not a reference to a string literal. String literals are not std::string
objects, so a temporary, unnamed std::string
object object is constructed and name
is a reference to that object. Your lambda then captures a reference to that same temporary object.
Such temporary objects' lifetime ends at the end of the full expression in which they're created, so that object's lifetime ends after the func = foo("test");
expression. If you call func
after that point then you will access the temporary object after its lifetime has ended, which results in undefined behavior.
The behavior of your second program is also undefined. Your lambda holds a reference to test
, and that object's lifetime ends at the end of its enclosing scope. Calling func
after that point means accessing test
after its lifetime has ended, leading to undefined behavior.
String literals are not std::string
objects. They are simple arrays of const char
s. That is, the type of "test"
is const char[5]
. That array has static storage duration. If you were to capture a reference to that array then you would be safe; i.e. the following is well-defined:
// -----------------------vvvvvvvvvvvvvvvvvvvvv Note this type is changed
std::function<void()> foo(const char (&name)[5]){
// capture name by reference
return [&](){
std::cout<<"The name is "<<name<<std::endl;
};
}
int main() {
std::function<void()> func;
{
func = foo("test");
func(); // func called in the same scope
}
func(); // func called in the outer scope
}