2

Today I see this piece of code and I'm wondering to know what it is exactly doing this const reference in an assignment where a new object is created. (I don't know how to name this kind of assignments.)

std::string const& p = s.c_str(); // s is a std::string

I understand that something like std::string const& p = s; will create a reference p to s, but in the line shown we are creating a new object (using the raw pointer from std::string::c_str).

I've made a MCVE in Coliru with this:

#include <iostream>
#include <string>

void foo(std::string const& s)
{
    std::string const& p = s.c_str(); // << here
    
    std::cout << s << " " << p << " " << &s << " " << &p << std::endl;
}

int main()
{
    foo("hello");
}

And, as expected the output is showing that a new object was created:

hello hello 0x7ffdd54ef9a0 0x7ffdd54ef950

So, my question is: Is this actually doing something I'm not able to see? Does it have any problem (like a dangling reference) in the code?

cbuchart
  • 10,847
  • 9
  • 53
  • 93
  • 1
    I once stumbled into a Q/A where the life-time of a temporary in a const reference was discussed. To me, it was surprising to learn that the life-time is in fact until end of scope where the const reference is defined in. [Does a const reference class member prolong the life of a temporary?](https://stackoverflow.com/a/2784304/7478597), [Returning temporary object and binding to const reference](https://stackoverflow.com/a/11560472/7478597), [GotW #88: A Candidate For the “Most Important const”](https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/) – Scheff's Cat Jan 18 '22 at 09:49
  • Thanks a lot! This clarifies a lot too – cbuchart Jan 18 '22 at 10:50

2 Answers2

4

From std::string::c_str's documentation, it returns:

a pointer to an array that contains a null-terminated sequence of characters (i.e., a C-string) representing the current value of the string object. That is, a const char*.

So when you wrote:

std::string const& p = s.c_str();

In the above statement, the const char* that was returned on the right hand side is used to create a temporary object of type std::string using a converting constructor that takes const char* as argument.

Next, the lvalue reference p on the left hand side is bound to that temporary obect. And in doing so, the lifetime of the temporary is extended.

To answer your last question, there is no dangling reference in your program.

Jason
  • 36,170
  • 5
  • 26
  • 60
3

In fact this construction

std::string const& p = s.c_str();

does not differ essentially from

std::string const p( s.c_str() );

but is more confusing.

As for the output of this statement

std::cout << s << " " << p << " " << &s << " " << &p << std::endl;

then there are outputted two different addresses. The first one is the address of the parameter s and the second one is the address of the temporary object of the type std::string created in this declaration

std::string const& p = s.c_str();

by means of the conversion constructor

basic_string(const charT* s, const Allocator& a = Allocator());
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • *"more confusing"* is opinion biased ;-) On the other side, it has the advantage to be sure that no **extra** copies happens (for example, in case the method returns already a const reference, instead of a temporary, especially when used with `auto`). – Jarod42 Jan 18 '22 at 10:22
  • Or better `std::string const&& ptemp( s.c_str() ); std::string const& p = ptemp;`? – Sebastian Jan 18 '22 at 10:40