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

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.

Related

Why is a static const char * const variable bindable to an rvalue reference parameter when it is an lvalue?

Given static const char * const x = "test"; and a function with the signature void DoSomething(std::string && value), why is it legal to bind this lvalue to the parameter like so DoSomething(x);?
I was under the impression that the string literal is an array of char but it decays to the pointer type and is still an lvalue. I'm just confused why this is legal.
When the function with an rvalue reference parameter expects to take ownership of the parameter's data, how does this work with memory in read only segments of say a PE file? I understand the memory isn't physically moved, but it seems like this would cause problems.
std::string is different to const char *. When you initialize a reference with an expression of different type that it can't bind directly to, then a temporary is created which has the correct type for the reference. The temporary is initialized by the initializer provided, and the reference is bound directly to the temporary. Rvalue references can bind to temporaries.
The function could then take ownership of the temporary's data. The string literal is unchanged (because the constructor string::string(const char *) does not change the literal, and instead takes a copy of its content).

STL Push_back string in vector

I am trying to push a string in a string vector, like below
void Node::set_val(string &val)
{
this->val.push_back(val);
}
But when I try to call it as below
Obj.set_val("10h;");
I get the below error,
error: no matching function for call to 'Node::set_val(const char [5])'
I assumed that the string in " " is same as string in c++, Why do I get such an error? What has to be changed below?
You are taking in a std::string by non-const reference. Non-const references cannot bind to rvalues, like "10h;", so you can't pass literals in to that function.
If you aren't going to modify the argument, you should take your argument by reference-to-const:
void Node::set_val(const string &val)
// ^^^^^
This way, a temporary std::string will be constructed from your const char[5] and passed in to set_val.
You could improve this by taking in the string by value and moveing it into the vector:
void Node::set_val(string val)
{
this->val.push_back(std::move(val));
}
This prevents you from making some unnecessary copies.
So in C++, const char* is implicitly convertible to std::string because std::string has a (non-explicit) constructor that takes const char*. So what the compiler tries here is to create a temporary std::string object for your function call, like so:
Node.set_val(std::string("10h;"));
However, since you declared the parameter of set_val to be a non-const reference to a std::string, the compiler can't make this conversion work due to the fact that temporary objects can't be bound to non-const references.
There are three ways to make this work, depending on what you want to achieve:
void Node::set_val(const std::string& val) {}
void Node::set_val(std::string val) {}
void Node::set_val(std::string&& val) {}
All will compile (the last one requires C++11 or higher), but seeing your use case, I would recommend to use the second or third one. For an explanation why, try reading a little bit about move semantics in C++11.
The important thing to take away here is that const char* implicitly converts to std::string by creating a temporary object, and temporary objects can't be passed to functions taking non-const references.
You are passing "10h;" which is a const char array.
Fix it by passing a string: Obj.set_val(string("10h")); and edit function to take a string by value:
void Node::set_val(string val) { /* */ }
Or maybe better, edit your function to take a const string&:
void Node::set_val(const string &val) { /* */ }

Passing a returned string into a function as reference without assigning it to a string variable

I have a function defined as:
void func(string & str_alias)
{...}
And in my main function
int main()
{
string a;
func((a="Cat said: ")+"Meow");
}
The compiler would report that
no known conversion for argument 1 from ‘std::basic_string<char>’ to ‘std::string& {aka std::basic_string<char>&}’
Though I know if I change the main function into:
int main()
{
string a;
func(a=((a="Cat said: ")+"Meow"));
}
The code would pass with no issues. But I still wonder why the returned string cannot be passed to the function as a reference. Why do I have to assign it to another string variable?
Thanks.
As long you don't need to change the passed reference, you could easily avoid this by changing your function signature to
void func(const string & str_alias)
// ^^^^^
{...}
and simply call
func(string("Cat said: ") + "Meow");
(see live demo)
If you'll need to change the reference parameter, you must have an lvalue to be modified. Nevertheless writing
func(a=string("Cat said: ")+"Meow");
is sufficient (see the live demo).
If you make it take const reference to std::string, it should compile.
This is because the last thing you do in the first function call is calling std::string operator+(const std::string&, const char*), which as you see returns std::string, not reference, and since it not stored anywhere, it is rvalue, which can't be bound to lvalue-reference.
The second example compiles, because the last thing you do is assign it to the variable a, which calls std::string& operator=(const char*), which as you can see returns reference, so it can be used as non-const reference by itself.
Thanks to 0x499602D2 for correction.

Why it does not work when I pass reference to this function?

The code below:
void test(string &s){ // if the argument is "string s", it works
return test(s+',');
}
The compiler reports cannot find the function: test(std::basic_string).
I think the compiler would create a temporary string (== s+','), and I can pass its reference.
But it seems I am wrong. I do not know why I cannot pass the reference of this temporary string.
You can't bind a temporary to a non-constant reference. You could either take the argument by const reference (or, as you point out, by value)
void test(string const & s){ // or string s
return test(s+',');
}
or use a named variable rather than a temporary
void test(string & s){
std::string s2 = s + ',';
return test(s2);
}
As noted, at great length, in the comments, this code has undefined runtime behaviour and shouldn't be used in "real" code; it's purpose is just a minimal example of how to fix the observed compilation error
make it const:
void test(const std::string &s){ // if the argument is "string s", it works
return test(s+',');
}
But first you should have seen this question String Concatenation
concatenating strings using "+" in c++
Alternative Solution
void test(string &s)
{
s.append(",");
return test(s);
}
Standard C++ does not allow a non-const lvalue reference to be bound to an rvalue. In your example, the result of the expression s+',' is a temporary and thus an rvalue, so the compiler is required to discard the overload of test expecting an lvalue reference, leaving it no overload available to call. That's why it complains about not being able to find the function test.
To solve this issue you have to provide an overload whose parameter may be bound to an rvalue. As you realized yourself, expecting an argument by-copy works, but it may imply unnecessary overhead by calling copy/move-ctors. A better option would be to expect the argument by reference. Since const lvalue references may be bound to rvalues, declaring the function as follows solves the problem.
void test(std::string const& s)

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.