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
Related
I have a base class and its subclass:
class Base {
public:
virtual void hi() {
cout << "hi" << endl;
}
};
class Derived : public Base {
public:
void hi() override {
cout << "derived hi" << endl;
}
};
Trying to create a helper function that creates a unique pointer of a Derived object.
1) This one works:
std::unique_ptr<Base> GetDerived() {
return std::make_unique<Derived>();
}
2) But, this one fails to compile:
std::unique_ptr<Base> GetDerived2() {
auto a = std::make_unique<Derived>();
return a;
}
3) std::move works:
std::unique_ptr<Base> GetDerived3() {
auto a = std::make_unique<Derived>();
return std::move(a);
}
4) If I create a Base instance, both work:
std::unique_ptr<Base> GetDerived4() {
auto a = std::make_unique<Base>();
return a;
}
std::unique_ptr<Base> GetDerived5() {
auto a = std::make_unique<Base>();
return std::move(a);
}
Why (2) fails but others work?
std::unique_ptr is not copyable, only movable. The reason you can return std::make_unique<Derived> from a function declared to return std::unique_ptr<Base> is that there is a conversion from one to the other.
So 1) is equivalent to:
std::unique_ptr<Base> GetDerived() {
return std::unique_ptr<Base>(std::make_unique<Derived>());
}
Since the value returned from std::make_unique is an rvalue, the return value is move-constructed.
Contrast that to 2), which is equivalent to:
std::unique_ptr<Base> GetDerived2() {
std::unique_ptr<Derived> a = std::make_unique<Derived>();
return std::unique_ptr<Base>(a);
}
Since a is an lvalue, the return value must be copy-constructed, and std::unique_ptr is non-copyable.
works because you cast the lvalue a to an rvalue, and the return value can be move-constructed.
and 5) work because you already have a std::unique_ptr<Base> and do not need to construct one to return.
In every case but (2) the returned value was treated as (some kind of) rvalue. In (2) it was not, because the types did not match the implicit move was blocked.
In a later iteration of the standard, (2) would also implicitly move.
All of them shortly engage in undefined behaviour after being called, as they try to delete a Derived object via pointer-to-Base. To fix this, record a deleter function.
template<class T>
using smart_unique=std::unique_ptr<T, void(*)(void*)>;
template<class T, class...Args>
smart_unique<T> make_smart_unique( Args&&... args ){
return {
new T(std::forward<Args>(args)...),
[](void*ptr){ delete static_cast<T*>(ptr); }
};
}
template<class T>
static const smart_unique<T> empty_smart_unique{ nullptr, [](void*){} };
These are unique pointers smart enough to handle polymorphism like shared_ptr can.
std::unique_ptr<> has no copy constructor, but it does have a move constructor from a related pointer, i.e.
unique_ptr( unique_ptr&& u ); // move ctor
template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ); // move ctor from related unique_ptr
The second constructor requires certain conditions (see here). So why did your code 2 not work, but 4 did? In 4, you didn't use any constructor, since the return type was identical to the object, the object itself was returned. In 2 on the other hand, the return type was different and a constructor call was needed, but that required std::move().
In the above listed example, (1) returns an rvalue but (2) is not an rvalue and is attempting a copy on the unique_ptr, which you cannot do for a unique_ptr.
Using move works because you are treating the unique_ptr at that point as an rvalue.
You will find that std::unique_ptr can move from derived's to base's, if you look into its definition. Basically is_convertible here check this situation.
/** #brief Converting constructor from another type
*
* Requires that the pointer owned by #p __u is convertible to the
* type of pointer owned by this object, #p __u does not own an array,
* and #p __u has a compatible deleter type.
*/
template<typename _Up, typename _Ep, typename = _Require<
__safe_conversion_up<_Up, _Ep>,
typename conditional<is_reference<_Dp>::value,
is_same<_Ep, _Dp>,
is_convertible<_Ep, _Dp>>::type>>
unique_ptr(unique_ptr<_Up, _Ep>&& __u) noexcept
: _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter()))
{ }
Let me have a custom wrapper container. I want to use it like this:
double d = 3.14;
MyContainer<std::vector<int>> pointer = new std::vector<int>();
MyContainer<std::string> rvalue = std::string("foo");
MyContainer<int> rvalue2 = 5 + 8;
MyContainer<double> lvalue = d;
I don't want to store copies of rvalues (a reference is OK). Rvalue references allow me to do like this:
std::string string1 = "foo";
std::string string2 = "bar";
std::string&& string3 = string1 + string2;
string3 += "test";
Basically I want to extend rvalues' lifetime to my container's lifetime. However when I do this:
template<class T>
class MyContainer {
public:
MyContainer(T&& obj) : object(obj) {}
T&& object
...
};
...
MyContaier<std::string> container = MyContainer(std::string("foo"));
I get an error (cannot bind 'std::string' lvalue to 'std::string&&'). The example is slightly different, but I just want to understand a general idea. How can I avoid this?
Besides that your code has multiple typos and syntax errors, there was nothing technically preventing you from taking an rvalue reference of std::string (although your assignment/constructor call is incorrect). Keeping a T&& as a member variable doesn't work like you think it does. Storing a reference to an expired rvalue and then accessing it would be UB as soon as you reach the next sequence point.
Here's a working example with a constructor for rvalue references and lvalue references. You need an actual instance inside your object if you want to "own" it. You can't technically extend the lifetime of an expiring rvalue; you can only construct something else that reuses it (and hopefully steals some of its expensive guts). Hope this helps.
#include <utility>
#include <string>
#include <iostream>
template<class T>
class MyContainer {
public:
// makes a copy from an lvalue reference
MyContainer(const T& obj)
: object(obj) {
}
// moves from an rvalue reference
MyContainer(T&& obj)
: object(std::move(obj)) {
}
MyContainer& operator=(const T& obj) {
object = obj;
}
MyContainer& operator=(T&& obj) {
object = std::move(obj);
}
T object;
};
int main() {
MyContainer<std::string> container = std::string("foo");
std::cout << container.object;
}
I have a class with copy & move ctor deleted.
struct A
{
A(int a):data(a){}
~A(){ std::cout << "~A()" << this << " : " << data << std::endl; }
A(A const &obj) = delete;
A(A &&obj) = delete;
friend std::ostream & operator << ( std::ostream & out , A const & obj);
int data;
};
And I want to create a tuple with objects of this class. But the following does not compile:
auto p = std::tuple<A,A>(A{10},A{20});
On the other hand, the following does compile, but gives a surprising output.
int main() {
auto q = std::tuple<A&&,A&&>(A{100},A{200});
std::cout << "q created\n";
}
Output
~A()0x22fe10 : 100
~A()0x22fe30 : 200
q created
It means that dtor for objects is called as soon as tuple construction line ends. So, what is significance of a tuple of destroyed objects?
This is bad:
auto q = std::tuple<A&&,A&&>(A{100},A{200});
you are constructing a tuple of rvalue references to temporaries that get destroyed at the end of the expression, so you're left with dangling references.
The correct statement would be:
std::tuple<A, A> q(100, 200);
However, until quite recently, the above was not supported by the standard. In N4296, the wording around the relevant constructor for tuple is [tuple.cnstr]:
template <class... UTypes>
constexpr explicit tuple(UTypes&&... u);
Requires: sizeof...(Types) == sizeof...(UTypes). is_constructible<Ti, Ui&&>::value is true
for all i.
Effects: Initializes the elements in the tuple with the corresponding value in std::forward<UTypes>(u).
Remark: This constructor shall not participate in overload resolution unless each type in UTypes is
implicitly convertible to its corresponding type in Types.
So, this constructor was not participating in overload resolution because int is not implicitly convertible to A. This has been resolved by the adoption of Improving pair and tuple, which addressed precisely your use-case:
struct D { D(int); D(const D&) = delete; };
std::tuple<D> td(12); // Error
The new wording for this constructor is, from N4527:
Remarks: This constructor shall not participate in overload resolution unless sizeof...(Types) >= 1 and is_constructible<Ti, Ui&&>::value is true for all i. The constructor is explicit if and only
if is_convertible<Ui&&, Ti>::value is false for at least one i.
And is_constructible<A, int&&>::value is true.
To present the difference another way, here is an extremely stripped down tuple implementation:
struct D { D(int ) {} D(const D& ) = delete; };
template <typename T>
struct Tuple {
Tuple(const T& t)
: T(t)
{ }
template <typename U,
#ifdef USE_OLD_RULES
typename = std::enable_if_t<std::is_convertible<U, T>::value>
#else
typename = std::enable_if_t<std::is_constructible<T, U&&>::value>
#endif
>
Tuple(U&& u)
: t(std::forward<U>(u))
{ }
T t;
};
int main()
{
Tuple<D> t(12);
}
If USE_OLD_RULES is defined, the first constructor is the only viable constructor and hence the code will not compile since D is noncopyable. Otherwise, the second constructor is the best viable candidate and that one is well-formed.
The adoption was recent enough that neither gcc 5.2 nor clang 3.6 actually will compile this example yet. So you will either need a newer compiler than that (gcc 6.0 works) or come up with a different design.
Your problem is that you explicitly asked for a tuple of rvalue references, and a rvalue reference is not that far from a pointer.
So auto q = std::tuple<A&&,A&&>(A{100},A{200}); creates two A objects, takes (rvalue) references to them, build the tuple with the references... and destroys the temporary objects, leaving you with two dangling references
Even if it is said to be more secure than good old C and its dangling pointers, C++ still allows programmer to write wrong programs.
Anyway, the following would make sense (note usage of A& and not A&&):
int main() {
A a(100), b(100); // Ok, a and b will leave as long as main
auto q = tuple<A&, A&>(a, b); // ok, q contains references to a and b
...
return 0; // Ok, q, a and b will be destroyed
}
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&&.
#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.