modifying object value through rvalue - c++

From Latest C++ ISO Standart
https://timsong-cpp.github.io/cppwp/expr#basic.lval-10
An lvalue is modifiable unless its type is const-qualified or is a function type. [ Note: A program that attempts to modify an object through a nonmodifiable lvalue or through an rvalue is ill-formed
But the following code below which modifies temporarily through rvalue runs well.
https://godbolt.org/z/L9H06i
#include <iostream>
struct A
{
std::string s1;
A():s1("123") {}
A&& modify() { s1 = "123411111111111111111111111111111111111111111111111111111111111111111111";
return std::move(*this);
} //modifying temporary object through rvalue!
};
void f(A&& o)
{
std::cout << o.s1.c_str();
}
int main()
{
f(A().modify());//modifying temporary object through rvalue!
return 0;
}

Related

Why is const temporary bound to rvalue reference parameter?

I have the following functions:
void func(void * const &ptr)
{
std::cerr << "const" << std::endl;
}
void func(void * &&ptr)
{
std::cerr << "mutable" << std::endl;
}
void* const func2()
{
return nullptr;
}
One overload takes const reference parameter and another takes mutable rvalue reference. And there is a function that returns const value.
When I pass that const temporary value to the function:
func(func2());
I expect the const overload to be chosen. But instead I get:
mutable
How is that possible? Why is const return value bound to non-const rvalue reference parameter?
This doesn't however happen when instead of void* I pass const struct to the function:
struct A
{
};
void func(A const &a)
{
std::cerr << "const" << std::endl;
}
void func(A &&a)
{
std::cerr << "mutable" << std::endl;
}
A const func3()
{
return A();
}
int main()
{
func(func3());
return 0;
}
The result is:
const
You can check this on coliru.
What is difference between const void* and const struct?
Is there a way to make overload that takes specifically const values?
Why is const temporary bound to rvalue reference parameter?
Because it's not const by the time overload resolution happens.
[expr.type]
2 If a prvalue initially has the type “cv T”, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.
A class type prvalue retains its cv-qualifications, but not a void* const prvalue. Overload resolution therefore happens with a plain void* prvalue, which explains the behavior you observe when the rvalue overload is chosen.
The types this paragraph applies to are those "fundamental" types whose value is actually accessed by the program. So a prvalue of such a type is indeed a "pure", ephemeral value, and cannot be modified already.

friend functions with const parameters

I got to know that to make a friend function, friend function should be explicitly declared in enclosing scope or take an argument of its class. However, this one seems to be a caveat, I am failed to understand. Why does the call to f1(99) not works?
class X {
public:
X(int i) {
std::cout << "Ctor called" << std::endl;
}
friend int f1(X&);
friend int f2(const X&);
friend int f3(X);
};
int f1(X& a) {
std::cout << "non-const called" << std::endl;
}
int f2(const X& a) {
std::cout << "const called" << std::endl;
}
int f3(X a) {
std::cout << "object called" << std::endl;
}
int main() {
f1(99);
f2(99);
f3(99);
}
You're calling the functions with argument 99; but all the functions expect an X. 99 could convert to X implicitly, but the converted X is a temporary object and can't be bound to lvalue-reference to non-const.
The temporary object could be bound to lvalue-reference to const (and also rvalue-reference since C++11), then f2(99); works. And it could be copied to the parameter of f3, then f3(99) works too.
The effects of reference initialization are:
Otherwise, if the reference is lvalue reference to a non-volatile const-qualified type or rvalue reference (since C++11):
Otherwise, object is implicitly converted to T. The reference is bound to the result of the conversion (after materializing a temporary) (since C++17). If the object (or, if the conversion is
done by user-defined conversion, the result of the conversion
function) is of type T or derived from T, it must be equally or less
cv-qualified than T, and, if the reference is an rvalue reference, must not be an lvalue (since C++11).

Const and reference member function qualifiers

Let's say we have member class with two member functions defined as follows:
class SomeClass
{
private:
int val = {};
public:
const int getVarLRef() & {
return val;
}
const int getVarCLRef() const& {
return val;
}
};
int main()
{
auto var1 = SomeClass().getVarCLRef();
auto var2 = SomeClass().getVarLRef();
return 0;
}
I not quite understand what is the difference between const& and &.
Why it works with getVarCLRef if we specified this function as const&? Shouldn't it be allowed to be invoked only with lvalues?
getVarLRef, on the other hand, works just fine and fails to compile in this case as expected.
I use C++11 and gcc 7.3.0
Const and reference member function qualifiers are to be able to apply those qualifier to "this" as for regular parameter, so mainly, you have something like:
int getVarLRef(SomeClass& self) { return self.val; }
int getVarCLRef(const SomeClass& self) { return self.val; }
And there, I think you know that:
getVarCLRef(SomeClass()); // Valid, temporary can bind to const lvalue reference
getVarLRef(SomeClass()); // INVALID, temporary CANNOT bind to non-const lvalue reference
Shouldn't it be allowed to be invoked only with lvalues?
Because rvalue could be bound to lvalue-reference to const too. Just as the following code works.
const SomeClass& r = SomeClass();
On the other hand, rvalue can't be bound to lvalue-reference to non-const, then the invocation of getVarLRef fails as you expected.

Using temporary object by non-const reference within a single statement in C++

Here is the code:
namespace NS
{
class B
{
public:
template<typename T> friend B& operator<<(B& b, T const& obj);
};
template<typename T> B& operator<<(B& b, T const& obj)
{
// using b and obj
return b;
}
}
int main()
{
NS::B() << 2.71 << 123 << "abcdef"; // doesn't compile in gcc
NS::B b; b << 2.71 << 123 << "abcdef"; // OK
}
The first line in main compiles in VS2015 (and some earlier ones) and doesn't compile in gcc (I tried in 6.3, 4.3.2).
Which compiler does the right thing?
I thought the temporary object's (NS::B()) lifetime would be up to the end of the statement (;), so it's ok to pass it to my operator<< by non-const reference.
If it isn't so, could you tell me why?
Thanks.
The standard says that temporary objects may not be converted to non-const lvalue references implicitly. See [dcl.init.ref]/8.6.3.5.2 in n4628.
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be
const), or the reference shall be an rvalue reference. [ Example:
double& rd2 = 2.0; // error: not an lvalue and reference not const
int i = 2;
double& rd3 = i; // error: type mismatch and reference not const
— end example ]
To solve this:
template<class T>
T& as_lvalue(T&&t){return t;}
This function takes an rvalue t and returns an lvalue reference.
as_lvalue(NS::B()) << 2.71 << 123 << "abcdef";
The above is both standard compliant and safe.

binding of lvalue into rvalue reference move ctor and return of function

According to what I know it is not valid to bind a lvalue into a rvalue reference.
And secondly, a lvalue expression is recognizable by the fact it can be prefix by adress-of operator, (&)
I'm a little bit in trouble if this two sentences arecorrect with the following codes :
#include<iostream>
struct Foo
{
Foo(Foo&& other)
{
std::cout << "move ctor called";
}
Foo(const Foo& other)
{
std::cout << "copy ctor called";
}
Foo(){}
};
Foo return_foo()
{
Foo f;
return f;
}
void main()
{
Foo f = return_foo(); // Move ctor is called, but return_foo() is a lvalue ??
std::cin.ignore();
}
Where I am wrong ?
return_foo() returns a prvalue (because it returns unnamed temporary object). Quote from §3.10/1, emphasis mine:
A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. [
Example: The result of calling a function whose return type is not a
reference is a prvalue. The value of a literal such as 12, 7.3e5, or
true is also a prvalue. —end example ]
There is a special rule which allows returning a temporary as an rvalue, namely, the following are equivalent - the explicit "I don't need this anymore" version:
T foo()
{
T t(a, b, ...); // constructed somehow
/* ... */
return std::move(t);
}
int main()
{
T t = foo(); // we can move-construct this
}
... and the implicit version:
T foo()
{
T t(a, b, ...);
/* ... */
return t; // implicitly allow moving
}
All this happens after return-value optimization. This means that returning by value is actually pretty efficient in many situations.