c++c++11

Reference to a string literal


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
}

Solution

  • 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 chars. 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
    }