string can not be assigned in a compund statement - c++

I don't really understand why this is happening.
I have some functions which are declared like this:
std::string unmaskString(std::string &oValue);
In the code I do this:
v = unmaskString(line.substr(eq+1));
and I get a compile error saying:
error: invalid initialization of non-const reference of type 'std::string&' from a temporary of type 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >'
When I put this in two separate statements it works:
v = line.substr(eq+1);
v = unmaskString(v);
The first line returns a string object, not even a reference, so I don't really understand the error.
Changing the function to
std::string unmaskString(std::string oValue);
also gives that error.
UPDATE:
Changed maskString to unmaskString as this was a mistake, but the problem still aplies as the masString has the same signature.

The result of:
line.substr(eq+1)
Is a temporary object of type std::string. Temporaries are rvalues, and lvalue references cannot bind to rvalues.
Notice, that if your maskString() function does not need to modify its argument (why would it return an std::string otherwise?), there is no reason for it to accept its argument as a reference to non-const.
The possible solutions are (in order of preference):
Let maskString() take its input by value, so that the input argument will be copied if it is an lvalue and moved if it is an rvalue:
std::string maskString(std::string oValue);
// ^^^^^^^^^^^
{
// Whatever is going on...
return oValue; // This will be MOVED into the object that stores
// the value returned by this function
}
Let maskString() take its input by lvalue reference to const (this way the initialization of value from oValue will always result in a copy, even if the argument is a temporary), then copy it into a temporary variable that will be eventually returned and moved from. This would work because lvalue references to const can bind to rvalues (and therefore to temporaries):
std::string maskString(std::string const& oValue);
// ^^^^^
{
std::string value = oValue;
// Whatever is going on...
return value; // This will be MOVED into the object that stores
// the value returned by this function
}
Do what you did: store the object returned by substr in a named object, and pass that object to unmaskString().

Related

C++ - Does passing by reference utilize implicit conversion?

I am trying to get a better understanding of what "passing by reference" really does in c++. In the following code:
#include <iostream>
void func(int& refVar) {
std::cout << refVar;
}
int main() {
int val = 3;
func(val);
}
How does func taking in a reference change the behavior of val when func is called? Is the val variable implicitly converted to a "reference" type and then passed into the function, just like how val would be converted to a float if func took in a float instead of an int? Or is it something else?
Similarly, in this code:
int val = 3;
int& ref = val;
How is a reference of val assigned to ref? Is there implicit type conversion that can be also achieved manually using a function, or does the compiler realize without converting that ref is a reference and that it needs to store the address of val?
Why don't you just try it?
https://godbolt.org/z/8or3qfd5G
Note that the compiler could do implicit conversion and pass a reference to the temporary.
But the only (good) reason to request a reference is to either store the reference for later use or modify the value. The former would produce a dangling reference and the later would modify a value that will be deleted when the function returns and can never be accessed. So effectively this construct is just bad.
The C++ gods have therefore decided that you aren't allowed to use this. Implicit conversion produces an rvalue and you can only bind a const reference to an rvalue.
Binding a rvalue to a const reference is still dangerous. You should not store a const reference for later use because it can become dangling.
Update: I noticed I never explained how calling a function taking a reference or assigning to a reference works. It's basically both the same thing.
A reference just gives something a(nother) name. There is no type change or casting or anything involved. So when you have
`func(val)`
then for the duration of the function the value in val has a second name refVar. Same with int & refVal = val;. There now is a second name for the value in val called refVal.
Afaik they are totally interchangeable.
Note: In a function call how it works is implementation detail but most compilers pass a int * to the function under the hood.

Pass by value/reference/rvalue with a std::move(str) arg

I have the following code:
//void func(const std::string &&i){
//void func(const std::string &i){
void func(const std::string i){
std::string val{i};
}
int main()
{
std::string d = "asdf";
func(std::move(d));
std::cout << d << std::endl;
}
When i is pass-by-value, d becomes empty, but d retains its form if we're passing by reference or by r-value reference. Could someone explain what is going on?
I understand that std::move doesn't actually move anything, but rather makes the variable it takes in moveable by casting it to an xvalue.
As an aside why does the code in the current state compile if d is cast to an x-value? func is currently set to take in pass by value arguments, and not by rvalue reference.
When i is pass-by-value, d becomes empty,
To be accurate, d will be in some valid state not specified in the C++ standard. Empty is one possibility.
std::move itself never causes the move constructor to be called directly. Neither does binding an rvalue reference to an object cause move constructor to be called directly.
Only initialising an object with a non-const rvalue will cause the argument to be moved from. In the example, std::string i is initialised with a non-const rvalue and the move constructor will be called.
As an aside why does the code in the current state compile if d is cast to an x-value?
Because the type has a (non-deleted) move constructor. Therefore the argument can be initialised from an rvalues.
I had thought if we had std::string i, a copy of the rvalue reference is made.
std::string i is not a reference. It is a variable of type std::string and as such there is an object of type std::string associated with the variable. That object is initialised with the expression that is passed into the function as argument.
Also, if I observe that the output of d is still the same as prior to applying std::move, what does this mean in this case?
If you call the uncommented version of the function with an rvalue, then the argument will be moved from. If the value is same as it was, then it simply means that the value is the same. You cannot assume that the value will be the same nor that it won't be the same.
Does it mean that d is still occupying the space it originally occupied?
Assuming that by "space" you mean the storage where the variable is, then of course it is still occupying the same storage. The address of an object never changes through the lifetime of the object.
void func(const std::string &&i)
This signature will not move anything, because the reference is to a const object. Remove the const, and it'll work. But only if you std::move the parameter i again inside the function. This is because anything that has a name is an lvalue, whether the parameter was declared as & or &&. See this answer.
void func(const std::string &i)
This will copy, as you probably already know. However, it behaves similarly to the ptevious one in that if you drop the const and do std::move( i ) inside the function, it'll actually move. This is because, as you noted, move is a cast and the compiler will listen to you and do exactly what you say when you cast, regardless of what you intended.
void func(const std::string i)
This moves in your example because here, i is an entirely new string. The outside string d gets moved into i. However, you still have to drop the const and use std::move( i ) if you want to move i into val.

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).

invalid initialization of non-const reference of type std::string& - how to remove

I am getting an error:
error: invalid initialization of non-const reference of type 'std::string& {aka std::basic_string&}' from an rvalue of type 'std::basic_string'
The code is:
const std::string& hitVarNameConst = (isKO ? "isKO" : "isKI");
for (int i = 0; i < numAssets; i++){
std::string& hitVarName1 = hitVarNameConst + Format::toString(i + 1);
//use hitVarname1 with some other function
}
How do I remove this error? Earlier I was trying the following code, it was still throwing the same error:
for (int i = 0; i < numAssets; i++){
std::string& hitVarName1 = (isKO ? "isKO" : "isKI") + Format::toString(i + 1);
//use hitVarname1 with some other function
}
Neither one of those strings should be a reference. They're objects, pure and simple. Remove the two &s.
You are creating a new string within your loop every time, so creating a reference to it means you are trying to create a reference to an r-value, which cannot be referenced since it has no address (until it is assigned).
Remove the & from the declaration type in your assignment and it should work, or change it to const std::string&, since compilers often allow temporaries to be assigned as references as long as they are constant.
When you have something like hitVarNameConst + Format::toString(i + 1) you get a temporary object out of it. A temporary object in terms of C++ is called an rvalue (rvalue is a historic term, referring to the fact that usually rvalues are found on the right-hand side of the assignment operator (as in a = get_b()). On the left-hand side you have an rvalue.
Non-const references can not be bound to rvalues. You can either have a const reference (which will have a nice effect of extending the lifetime of the temporary) or you can create a new instance of std::string and copy your temporary there. Copy-elision together with move semantics will ensure no real copying will be taking place.

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.