What's the difference between these two local variables? - c++

const std::string s1("foo");
const std::string& s2("foo");
Not sure how they are different but I'm seeing evidence of both usages. Any ideas?

const std::string s1("foo");
This declares a named std::string object as a local variable.
const std::string& s1("foo");
This declares a const reference to a std::string object. An unnamed, temporary std::string object is created with the contents "foo" and the reference is bound to that temporary object. The temporary object will exist until the reference goes out of scope.
In this particular case, there is no observable difference between the two: in both cases you end up with a std::string that can be accessed via the name s1 and which will be destroyed when s1 goes out of scope.
However, in some cases, there is a difference. Consider, for example, a function that returns by reference:
const std::string& get_reference_to_string();
If you initialize s1 with the result of calling this function, there is a difference between:
const std::string s1(get_reference_to_string());
const std::string& s1(get_reference_to_string());
In the first case, a copy of the referenced string is made and used to initialize s1. In the second case, s1 is simply bound to the std::string to which the returned reference refers: no copy is made.

They are identical in meaning, due to the way constant references can bind to temporaries plus string having an implicit conversion from char*.
For clarity and readability, prefer the non-reference.

The first instance creates a constant string variable initialized to hold "foo". The second one creates a constant reference to a string and then initializes that constant reference to point at a temporary string that was initialized to hold "foo".

const std::string s1("foo"); creates a std::string object on the stack and initializes it with the const char* string "foo." const std::string& s1("foo"), on the other hand, is a const reference to an implicitly constructed std::string.

Related

Understanding assignment of const reference to non-cost data member

I am reviewing C++ references any am trying to reason why the following piece of code complies:
#include <string>
class Foo {
public:
Foo(const std::string& label)
: d_label(label) {}
private:
std::string d_label;
};
int main(int argc, const char** argv) {
Foo("test");
return 0;
}
Here, we are assigning a reference to a const string to a string. In doing so, is a copy of label made that is non-const? If so, why is it that we can make a copy of a const object that is itself non-const? Otherwise, what exactly is going on here, in terms of copy constructor/assignment calls?
In C++ the keyword const actually means read-only. To make a copy of an object, you don't need write access. Therefore, you can copy a const std::string into an std::string.
Also note, that copying in C++ means making a deep copy by default. This is called value semantics. Hence, manipulating the copied string will not do anything to the original string.
Now to your last question: What is going on in the following line?
Foo("test");
The type of "test" is const char[5]. The compiler searches for a matching constructor of Foo. Since "test" is implicitly convertible to std::string via the
basic_string<CharT,Alloc>::basic_string( const CharT * s, const Alloc & a = Alloc() );
constructor, this conversion will be performed, i. e. a temporary std::string is constructed from "test". A const reference to this temporary is then passed to the constructor
Foo::Foo( const std::string & label );
This constructor in turn calls std::strings copy constructor in order to construct the d_label member of Foo:
basic_string<CharT,Alloc>::basic_string( const basic_string & other );
C++ offers both reference semantics and value semantics: Objects have values, and then you can take references to objects.
std::string d_label
d_label is an object holding a string value. It is considered to own the bytes holding the string as a memory resource. This notion of ownership rationalizes using d_label as an interface to modify the string.
const std::string& label
label is a read-only reference to a string. This is not quite the same as "a reference to a const string." It refers to an object that may be (and probably is) not const.
: d_label(label)
This initializes d_label with the content of label using a copy constructor. You may then do what you like with the copy. You know that the process of copying won't modify the underlying object of label because label was declared const &.
Here no assignments take place. d_label(label) initializes the variable d_label with label which subsequently ends up calling copy constructor of string type.
Let's have a closer look:
Foo("test"); initializes const std::string& label with "test" which its type is const char[5]
Here a string reference (label) is getting initialized with a value having type const char[5]. This initialization is valid since "test" through decaying to const char * can be passed to one of string constructors which gets a const char *.
Now label is a reference to a real string object storing "test".
Then, d_label(label) initializes Foo::d_label with the object referred to by label.
At this point copy constructor of string type is called and constructs the Foo::d_label.

C++ const char* to std::string &

I have a code that compiles, and it looks like:
void test1(const std::string &name)................
test1("myName");
But, if I remove the const from the declaration as:
void test2(std::string &name)................
test2("myName");
The code just doesn't compile.
Why the const std::string makes it compile? As far as I understand the same implicit constructor will be called for the char * to be converted to a std::string.
I am using Visual Studio 2013, in case this might be related to the answer.
When the implicit conversion happens, you get a temporary std::string (technically, the important thing is that it is an xvalue). You cannot bind a temporary to a lvalue reference std::string & but you can bind to a const lvalue reference const std::string & or an rvalue reference std::string &&.
The same thing happens here:
const int &x = 5;
int &x = 5; // error
You get an error in second case because a temporary can not bind to a non-const reference.
test("myName"); tries to bind temporary constructed object to a lvalue in second case, hence an error
"myName" is a c-style string and it has the type of const char[]. Since it is not a std::string it needs to be converted to one. When that happens a temporary string is created. That temporary string cannot be bound to a reference but it can be bound to a const reference as it will extend its lifetime to the end of the expression.
You could also pass the string by rvalue reference like void foo(std::string && bar). This will move the temporary string into the function and give you the same effect.
A third option is to pass the string by value void foo(std::string bar). This works with temporaries and non temporaries and will wither make a copy or a move depending on the source of the string.
The problem is with the & you are using for reference. When calling the function with a string literal you cannot pass a non-const reference.

initializing references in c++ doesn't work but initializing const references works, why?

const string& s = "rajat";
works while
string& s = "rajat";
doesn't. Why?
"rajat" is not a std::string, it is a null-terminated array of six char, i.e. char[6]
You can construct a std::string from a null-terminated array of char, and that's what happens when you write:
std::string s = "rajat";
When you want to initialize a string& you have to have a string for the reference to bind to, so the compiler tries to construct a string from the char array and bind the reference to that i.e.
std::string& s = std::string("rajat");
However this is illegal because the string that gets constructed is a temporary object and non-const references cannot bind to temporary objects, see How come a non-const reference cannot bind to a temporary object?
This will implicitly construct a temporary string from the string literal on the RHS. The temporary is then bound to a reference:
const string& s = "rajat";
// ^^^^^ temporary string is constructed from "rajat" literal
The language only allows const references to bind to temporaries, so this
string& s = "rajat";
is illegal, since it attempts to bind a non-const reference to a temporary string.
See this related GotW post, which also deals with lifetime issues.

When is a temporary used as an initializer for a named object destroyed?

In "The C++ Programming Language (3rd)" p.255:
A temporary can be used as an initializer for a const reference or a named object. For example:
void g(const string&, const string&);
void h(string& s1, string& s2)
{
const string& s = s1+s2;
string ss = s1+s2;
g(s, ss); // we can use s and ss here
}
This is fine. The temporary is destroyed when "its" reference or named object go out of scope.
Is he saying that the temporary object created by s1+s2 is destroyed when ss goes out of scope?
Isn't it get destroyed as soon as it is copy initialized to ss?
The only temporaries in your code are the s1 + s2. The first one gets bound to the const-ref s, and thus its lifetime is extended to that of s. Nothing else in your code is a temporary. In particular, neither s nor ss are temporaries, since they are manifestly named variables.
The second s1 + s2 is of course also a temporary, but it dies at the end of the line, having been used to initialize ss only.
Update: Perhaps one point deserves emphasis: In the final line, g(s, ss);, the point is that s is a perfectly valid reference, and it is not a dangling reference as you might perhaps have expected, precisely because of the life-time extension rule for temporaries bound to const-references.
Both are true, because two temporaries are created:
//creates a temporary that has its lifetime extended by the const &
const string& s = s1+s2;
//creates a temporary that is copied into ss and destroyed
string ss= s1+s2;

Why is it trying to send a char array instead of a string?

My function declaration is
siteObject(std::string& url, std::string& get, std::string& post);
So why is this site("String 1", "String 2", "String 3"); creating a mismatch type error. It says it wants a string reference and it's receiving a char array. If you need more detail just ask in the comments.
Because there's an implicit call to the std::string constructor, which creates a temporary object. You cannot take a non-const reference to a temporary (because it's meaningless to modify a temporary).
So, either modify your function to take const references, or by-value, or pass it non-temporary objects.
Your siteObject function:
siteObject(std::string& url, std::string& get, std::string& post);
takes non-const references to string objects, which cannot be bound to rvalues (or temporaries).
When you try to call the function with string literals, the compiler has to convert those arguments (which are char*) to something that matches the parameter types - that conversion results in a temporary std::string object.
You'll need to change your function to accept const references if you want to be able to bind them to temporaries:
siteObject(std::string const& url, std::string const& get, std::string const& post);
Or you could pass values instead of references:
siteObject(std::string url, std::string get, std::string post);
You need to either make your function accept const std::string& str or construct string instances to pass in, and not rely on the implicit conversion of char* to string objects.
The correct call is:
std::string url("...");
std::string get("...");
std::string post("...");
siteObject(url, get, post);
This makes sense since the method signature implies that you get something back in the three strings (non-const references) and you may use those return values.
If that's not the intention and you have the ability to change the siteObject() method then you should do:
siteObject(std::string const & url, std::string const & get, std::string const & post);
and use your original call.
Strings entered in double quote characters are coming from the C heritage of C++ (they are called C string or NUL terminated string). They are implemented by the compiler as array of char. On the contrary, the std::string is a C++ class that aims to simplify manipulation of strings. It owns a C string (and can be created from one since it has a constructor that accept a const char*), and manage its memory.
Since there exists a construtor of std::string from const char* and that C string are compatible with that type, how come the compiler cannot call this function ? This is because the function is taking non-const reference to std::string objects. The constructor can't be used in this situation because the objects created would be temporaries, and you cannot get a non-const reference to a temporary object, as the called function may mutate it.
You can either create the std::string and pass them to the function:
std::string s1("String 1");
std::string s2("String 2");
std::string s3("String 3");
site(s1, s2, s3);
Or you can change the prototype of the site function to accept const reference. This is only possible if the function does not mutate the objects and you have access to the code:
// Declaration
void site(const std::string& s1, const std::string& s2, const std::string s3);
// Usage
site("String 1", "String 2", "String 3");