rvalue reference as an object field - c++

#include <iostream>
struct Bar
{
int nb_;
Bar(int nb) :nb_(nb){}
~Bar()
{
std::cout << "~Bar" << "\n";
}
};
struct Foo
{
template<class T>
Foo(T&& param) :bar_(std::move(param))
{
std::cout << "Foo" << "\n";
}
~Foo()
{
std::cout << "~Foo" << "\n";
}
Bar&& bar_;
};
int main()
{
{
Foo foo(Bar(1));
}
std::cin.ignore();
}
//Output:
Foo
~Bar
~Foo
Is this program is legal C++? Does bar_ will become a dangling reference once the Foo constructor will be finished?
If this is not legal in term of C++ standard, when is it useful to have a rvalue reference as a field ?

Does bar_ will become a dangling reference once the Foo constructor will be finished?
Not quite. It becomes a dangling reference at the point where the temporary created by Bar(1) is destroyed: at the end of the full-expression Foo foo(Bar(1)).
That also shows a usage example of rvalue reference members, for example forward_as_tuple:
struct woof
{
using my_tuple = std::tuple<std::vector<int>, std::string>;
my_tuple m;
woof(my_tuple x) : m(std::move(x)) {}
};
std::vector<int> v{1,2,3,4,5};
woof w( forward_as_tuple(std::move(v), std::string{"hello world"}) );
forward_as_tuple can store rvalue references (here: in a tuple<vector<int>&&, string&&>), which still can be used by the ctor of woof to move v and the temporary created by std::string{"hello world"}.

Yes, bar_ will become a dangling reference after the constructor has finished.
It rarely makes sense to store a reference, but some cases exist. I don't think there is any difference between the different types of references. The stored reference references another object and you need to make sure that the object is still valid when the reference is used.
Note that if you write
Bar b( 1 );
Foo foo(std::move(b));
foo will not have moved b, it is just storing a reference to it. b is still valid and can be used. Only if you have a member of Foo which actually moves from the stored reference, b will be moved.

Related

Return rvalue reference from function

CPP Core guidelines F45 states:
Don't return a T&&.
I want to create some class, and pass the class through a sequence of functions that modify the class. Then I will either evaluate some members of that class, or store the class in some container, like std::vector.
I am trying to do this in a way that the class is constructed exactly once, then destroyed, or moved and the moved-from copy is destroyed, and later the stored copy is destroyed (when the container that I stored it in is destroyed).
In essence, I want to do this:
// some class
class foo{}
// construct a foo, with some parameters, and modify it somehow
auto f1 = modify_foo(foo(x, y, z));
// modify the foo some more
auto f2 = modify_foo(f1);
// modify the foo some more
auto f3 = modify_foo(f2);
// use some element of modified foo
auto v = f3.getx();
// maybe store the modified foo in a vector or some other container
vector<foo> vf;
vf.emplace_back(f3);
It should be possible to construct the foo exactly once, and move the constructed foo through any number of modifying functions, then destroy the foo exactly once.
In the case of storing the foo in a vector, an additional copy/move and destroy will have to occur.
I can achieve this behavior, but I can't figure out any way to do it without using this signature for the modify functions:
foo&& modify_foo(foo&& in);
Here is test code that seems to do what I want:
#include <iostream>
#include <functional>
#include <vector>
// A SIMPLE CLASS WITH INSTRUMENTED CONSTRUCTORS
class foo {
public:
// default constructor
foo() {
std::cout << "default construct\n";
}
// copy constructor
foo(foo const &in) : x{ in.x } {
std::cout << "copy construct\n";
};
// copy assignment
foo& operator=(foo const& in) {
x = in.x;
std::cout << "copy assignment\n";
}
// move constructor
foo(foo&& in) noexcept : x(std::move(in.x)) {
std::cout << "move constructor\n";
}
// move assignment
foo& operator=(foo&& in) noexcept {
x = std::move(in.x);
std::cout << "move assignment\n";
return *this;
}
// destructor
~foo() {
std::cout << "destructor\n";
}
void inc() {
++x;
}
int getx() { return x; };
private:
int x{ 0 };
};
Now a function that will take foo&&, modify foo, return foo&&:
// A SIMPLE FUNCTION THAT TAKES foo&&, modifies something, returns foo&&
foo&& modify(foo&& in) {
in.inc();
return std::move(in);
}
Now using the class and modify function:
int main(){
// construct a foo, modify it, return it as foo&&
auto&& foo1 = modify(foo());
// modify foo some more and return it
auto&& foo2 = modify(std::move(foo1));
// modify foo some more and return it
auto&& foo3 = modify(std::move(foo2));
// do something with the modified foo:
std::cout << foo3.getx();
}
This will do exactly what I want. It will call the constructor once, correctly print 3, and call the destructor once.
If I do the same thing, except add this:
std::vector<foo> fv;
fv.emplace_back(std::move(foo3));
It will add one move construct, and another destruct when the vector goes out of scope.
This is exactly the behavior I want, and I haven't figured out any other way to get there without returning foo&& from the modifier, and using auto&& for my intermediate variables, and using std::move() on the parameters being passed to the subsequent calls to modify.
This pattern is very useful to me. It is bothering me that I can't resolve this with CPP core guidelines F.45. The guideline does say:
Returning an rvalue reference is fine when the reference to the temporary is being passed "downward" to a callee; Then the temporary is guaranteed to outlive the function call...
Maybe that is what I am doing?
My questions:
Is there anything fundamentally wrong, or undefined, in what I am doing?
When I do auto&&, and hover over the foo1, it will show it as a foo&&. I still have to wrap the foo1 with std::move(foo1) to get the modify function to accept it as foo&&. I find this a little strange. What is the reason for requiring this syntax?
As was correctly pointed out by NathanOliver, attempting to use rvalue ref's was leaving a dangling reference to an object that was being destroyed at the end of the function's life.
The piece of the puzzle that I was missing was to use 'auto&', instead of 'auto' when returning a ref from a function:
// function taking lvalue ref, returning lvalue ref
foo& modify(foo& in) {
in.inc();
return in;
}
{
auto f = foo{}; // constructed here
auto f1 = modify(f); // <-- BAD!!! copy construct occurs here.
auto& f2 = modify(f); // <-- BETTER - no copy here
} // destruct, destruct
If I use auto& to capture the lvalue ref returning from 'modify', no copies are made. Then I get my desired behavior. One construct, one destruct.
{
// construct a foo
foo foo1{};
// modify some number of times
auto& foo2 = modify(std::move(foo1));
auto& foo3 = modify(std::move(foo2));
auto& foo4 = modify(std::move(foo3));
std::cout << foo4.getx();
} // 1 destruct here

Implementing move constructor

In the line commented by ***, why is Bar's copy constructor called? input_bar is a rvalue reference, so I expect the move constructor to be called. Did it convert to an lvalue reference? I can make the move constructor call if I change that line to bar_(std::move(input_bar)).
#include <iostream>
#include <array>
#include <memory>
class Bar
{
public:
Bar(const Bar& bar)
{
std::cout << "copy constructor called" << std::endl;
}
Bar(Bar&& bar)
{
std::cout << "move constructor called" << std::endl;
}
};
class Foo
{
public:
Foo(Bar&& input_bar) :
bar_(input_bar) // ***
{
}
Bar bar_;
};
int main()
{
Bar bar;
Foo foo(std::move(bar));
return 0;
}
Once an entity has a name, it is clearly an lvalue! If you have a name for an rvalue reference, the entity with the name is not an rvalue but an lvalue. The entire point is that you know that this entity references an rvalue and you can legitimately move its content.
If you want to just forward the rvalueness to the next function you call, you'd use std::move(), e.g.:
Foo(Bar&& bar): bar_(std::move(bar)) {}
Without the std::move() the rvalue is considered to be owned by the constructor. With the std::move() it releases the ownership and passes it on to the next function.
You have to move rhrs:
Foo(Bar&& input_bar) :
bar_(std::move(input_bar)) // ***
{
}
The reasoning is that once we actually use the RHR, it should be semantially treated as out of scope. Forcing you to use std::move allows the following code to not be undefined:
Foo(Bar&& input_bar) {
std::cout << input_bar.baz << std::endl;//not undefined
bar_ = Bar{std::move(input_bar)};//input_bar is now essentailly destroyted
//std::cout << input_bar.baz << std::endl;//Undefined behavior, don't do this
}
The general rule of thumb is that only things without names can actually be used as RHR's...RHR as arguments have names, and thus will be treated as LHR untill you call a function on them.

When is it ok to modify a value when you remove const with const_cast?

According to §7.1.​5.1/4:
Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.
So my question becomes: when is an object a const object?
In particular, is a const member in a non-const object considered a const object?
class Foo {
const Bar bar;
void replaceBar(Bar bar2) {
*(const_cast<Bar *>&bar) = bar2; // Undefined behavior?
}
}
This comes up because I have an immutable class (all fields are const), but I want to have a move constructor, which technically modifies the value passed in. I'm ok with "cheating" in that case, since it doesn't break logical constness.
The simple rule is: it is ok to cast away constness if the original object is not const. So if you have a non-cont object and, say, you pass the const reference to it to a function, it is legal to cast away constness in the function.
In your example the original object is const, so casting constness away is undefined behaviour.
Let us make this a full example:
struct Bar { int x; };
struct Foo {
const Bar bar;
Foo( int x ):bar(x) {}
void replaceBar(Bar bar2) {
*(const_cast<Bar *>&bar) = bar2; // Undefined behavior?
}
};
now, let us break the world.
int main() {
Foo f(3);
Bar b = {2};
f.replaceBar(b);
std::cout << f.bar.x << "\n";
}
the above can and probably should output 3, because a const object Bar was created with x=3. The compiler can, and should, assume that the const object will be unchanged throughout its lifetime.
Let's break the world more:
struct Bar {
int* x;
Bar(int * p):x(p) {}
~Bar(){ if (x) delete x; }
Bar(Bar&& o):x(o.x){o.x=nullptr;}
Bar& operator=(Bar&& o){
if (x) delete x;
x = o.x;
o.x = nullptr;
}
Bar(Bar const&)=delete;
Bar& operator=(Bar const&)=delete;
};
struct Foo {
const Bar bar;
Foo( int* x ):bar(x) {}
void replaceBar(Bar bar2) {
*(const_cast<Bar *>&bar) = bar2; // Undefined behavior?
}
};
now the same game can result in the compiler deleting something twice.
int main() {
int* p1 = new int(3);
Foo f( p1 );
Bar b( new int(2) );
f.replaceBar(std::move(b));
}
and the compiler will delete p1 once within replaceBar, and should delete it also at the end of main. It can do this, because you guaranteed that f.bar.x would remain unchanged (const) until the end of its scope, then you violated that promise in replaceBar.
Now, this is just things the compiler has reason to do: the compiler can literally do anything once you have modified an object that was declared const, as you have invoked undefined behavior. Nasal demons, time travel -- anything is up for grabs.
Compilers use the fact that some behavior is undefined (aka, not allowed) to optimize.

Temporary lifetime and perfect forwarding constructor

I am having trouble understanding why the lifetime of temporaries bound to const reference parameters is cut short when there is a perfect forwarding constructor around. First of, what we know about temporaries bound to reference parameters: they last for the full expression:
A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full expression containing the call
However I found cases where this is not true (or I might simply misunderstand what a full expression is). Let's take a simple example, first we define an object with verbose constructors and destructors:
struct A {
A(int &&) { cout << "create A" << endl; }
A(A&&) { cout << "move A" << endl; }
~A(){ cout << "kill A" << endl; }
};
And an object wrapper B, which will be used for reference collapsing:
template <class T> struct B {
T value;
B() : value() { cout << "new B" << endl; }
B(const T &__a) : value(__a) { cout << "create B" << endl; }
B(const B &p) = default;
B(B && o) = default;
~B(){ cout << "kill B" << endl; };
};
We can now use our wrapper to capture references on temporaries and use them in function calls, like so:
void foo(B<const A&> a){ cout << "Using A" << endl; }
int main(){ foo( {123} ); }
The program above prints what I would expect:
create A
create B
Using A
kill B
kill A
So far so good. Now let's move back to B and add a perfect forwarding constructor for convertible types:
template <class T> struct B {
/* ... */
template <class U, class = typename enable_if<is_convertible<U, T>::value>::type>
B(U &&v) : value(std::forward<U>(v)) {
cout << "new forward initialized B" << endl;
}
};
Compiling the same code again now gives:
create A
new forward initialized B
kill A
Using A
kill B
Note that our A object was now killed before it was used, which is bad! Why did the lifetime of the temporary not get extended to the full call of foo in this case? Also, there is no other call to the desctructor of A, so there is no other instance of it.
I can see two possible explanations:
either the types are not what I think they are: changing the convertible move constructor to B(T &&v) instead of template <class U>B(U &&v) solves the problem.
or {123} is not a subexpression of foo( {123} ). Swapping {123} for A(123) also solves the issue, which makes me wonder if brace-initializers are full expressions.
Could someone clarify what is going on here?
Does this mean that adding a forwarding constructor to a class could break backward compatibility in some cases, like it did for B?
You can find the full code here, with another test case crashing for references to strings.
The type inferred for U in the call to B<A const&>::B(U&&) is int, so the only temporary that can be lifetime-extended for the call to foo in main is a prvalue int temporary initialized to 123.
The member A const& value is bound to a temporary A, but that A is created in the mem-initializer-list of the constructor B<A const&>::B(U&&) so its lifetime is extended only for the duration of that member initialization [class.temporary]/5:
— A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits.
Note that a mem-initializer-list is the part after the colon in a ctor-initializer:
template <class U, class = typename enable_if<is_convertible<U, T>::value>::type>
B(U &&v) : value(std::forward<U>(v)) {
^--- ctor-initializer
^--- reference member
^--- temporary A
This is why kill A is printed after new forward initialized B.
Does this mean that adding a forwarding constructor to a class could break backward compatibility in some cases, like it did for B?
Yes. In this case it's difficult to see why the forwarding constructor would be necessary; it's certainly dangerous where you have a reference member that a temporary could be bound to.
void foo(B<const A&> b);
foo( {123} );
is semantically equivalent to:
B<const A&> b = {123};
that for a non-explicit constructor is semantically equivalent to:
B<const A&> b{123};
going further, since your forwarding-constructor takes anything, it actually is initialized with int, not A:
B<const A&>::B(int&& v)
That is, a temporary instance of A is created on the constructor's initialization list:
B(int&& v) : value(A{v}) {}
// created here ^ ^ destroyed here
which is legal, just like you can type const A& a{123};.
This A instance is destroyed after the B's construction is finished, and you end up with a dangling reference within the body of foo.
The situation changes when you build the instance in a call expression, then the A temporary ends its lifetime at the end of the call expression:
foo( A{123} );
// ^ A is destroyed here
so it stays alive within foo, and the forwarding-constructor selected for B<const A&> is instantiated with a type A&&.

Are objects inside rvalue referenced object, also rvalue referenced?

Are objects inside rvalue referenced object, also rvalue referenced?
struct A{
};
struct B{
A a2;
};
//template<class B>
void test(B &&b){
// 1. Is this the correct way?
auto &&in3 = std::forward<B>(b).a2;
std::cout << std::is_rvalue_reference<decltype(in3)>::value;
// return true
// 2. or this?
auto &&in4 = b.a2;
std::cout << std::is_rvalue_reference<decltype(in4)>::value;
// return false
}
test(B());
http://coliru.stacked-crooked.com/a/bcf0f7dc4cc0440e
Yes, members of rvalues are themselves rvalues. This was clarified by DR 421
But that is irrelevant here:
auto &&in4 = b.a2;
b is not an rvalue, it's an lvalue (simple rule of thumb: it has a name).
To restore the value category it had when passed to the function you need to forward it