Is There a Reference_Wrapper<> for Rvalue References

Is there a reference_wrapper for rvalue references?

My take on this.

20.8.10.1.2/10 in N3225

The values of the bound arguments v1, v2, ..., vN and their corresponding types V1, V2, ..., VN
depend on the types TiD derived from the call to bind and the cv-qualifiers cv of the call wrapper g as
follows:

  • if TiD is reference_wrapper, the argument is tid.get() and its type Vi is T&;
  • if the value of is_bind_expression::value is true, the argument is tid(std::forward(uj)...)
    and its type Vi is result_of::type;
  • if the value j of is_placeholder::value is not zero, the argument is std::forward(uj)
    and its type Vi is Uj&&;
  • otherwise, the value is tid and its type Vi is TiD cv &.

So the only possibility to have a rvalue reference is to have is_bind_expression<TiD>::value true or is_placeholder<TiD>::value not zero. The second possibility has implications you don't want and achieving the wanted result with the first would imply that the problem we are trying to solve is solved if we restrict to the standard provided types. So, the only possibility would be to provide your own wrapper and a specialisation for is_bind_expression<TiD> (that is allowed by 20.8.10.1.1/1) as I don't see one.

Trying to wrap std containers to store rvalue references (like unique_ptr, but on the stack)

If you want to avoid copy elements around you can use std::move.

So if you have a std::list you can populate it with values by moving them in:

SomeBigObject sbo;

std::list<SomeBigObject> list;
list.push_back(SomeBigObject()); // SomeBigObject() is a rvalue and so it is moved
list.push_back(std::move(sbo)); // sbo may not be a rvalue so you have to move it

// For construction you can also use std::list::emplace
list.emplace(list.end()); // construct the value directly at the end of the list

For accessing them you can simply use the ranged based loop:

for(auto& i :list)
...

If you want to move them out of the container you can also use std::move.
The object is moved out of the container but the remains will still be in the container,
so you have to erase them:

for(auto it = list.begin; it != lsit.end();)
{
// the value of *it is moved into obj;
// an empty value of "SomeBigObject" will remain so erase it from the list
SomeBigObject obj = std::move(*it);

it = list.erase(it);

// do something with "obj"
...
}

rvalue references, std::reference_wrappers and std::function

That is because none of the objects you create actually is a std::function, they are callables which can be used to create temporary std::functions. And the last bit to the best of my knowledge (e.g. I'm not claiming it's true, I'm assuming that due to my laziness, see below) is UB, as moved from object can be left in any valid state, so no guarantees that the string member will actually be empty.

As a rule of thumb, use moved from objects in ways that don't require any preconditions (reassign, check if empty for string / vec, etc. qualify).

To clarify, let's look at the std::function constuctors here, the constructor in question is (5):

template< class F >
function( F f );

So when you try to construct an std::function with a callable, you by default will create a copy of the callable. You circumvent it e.g. by using std::ref, which will cause the subsequent changes to the callable to be reflected in the std::function using it (as then you actually create "from ref", not by from copy as usual).

What is the difference between std::reference_wrapper and a simple pointer?

std::reference_wrapper is useful in combination with templates. It wraps an object by storing a pointer to it, allowing for reassignment and copy while mimicking its usual semantics. It also instructs certain library templates to store references instead of objects.

Consider the algorithms in the STL which copy functors: You can avoid that copy by simply passing a reference wrapper referring to the functor instead of the functor itself:

unsigned arr[10];
std::mt19937 myEngine;
std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine's state

This works because…

  • reference_wrappers overload operator() so they can be called just like the function objects they refer to:

    std::ref(myEngine)() // Valid expression, modifies myEngines state
  • …(un)like ordinary references, copying (and assigning) reference_wrappers just assigns the pointee.

    int i, j;
    auto r = std::ref(i); // r refers to i
    r = std::ref(j); // Okay; r refers to j
    r = std::cref(j); // Error: Cannot bind reference_wrapper<int> to <const int>

Copying a reference wrapper is practically equivalent to copying a pointer, which is as cheap as it gets. All the function calls inherent in using it (e.g. the ones to operator()) should be just inlined as they are one-liners.

reference_wrappers are created via std::ref and std::cref:

int i;
auto r = std::ref(i); // r is of type std::reference_wrapper<int>
auto r2 = std::cref(i); // r is of type std::reference_wrapper<const int>

The template argument specifies the type and cv-qualification of the object referred to; r2 refers to a const int and will only yield a reference to const int. Calls to reference wrappers with const functors in them will only call const member function operator()s.

Rvalue initializers are disallowed, as permitting them would do more harm than good. Since rvalues would be moved anyway (and with guaranteed copy elision even that's avoided partly), we don't improve the semantics; we can introduce dangling pointers though, as a reference wrapper does not extend the pointee's lifetime.

Library interaction

As mentioned before, one can instruct make_tuple to store a reference in the resulting tuple by passing the corresponding argument through a reference_wrapper:

int i;
auto t1 = std::make_tuple(i); // Copies i. Type of t1 is tuple<int>
auto t2 = std::make_tuple(std::ref(i)); // Saves a reference to i.
// Type of t2 is tuple<int&>

Note that this slightly differs from forward_as_tuple: Here, rvalues as arguments are not allowed.

std::bind shows the same behavior: It won't copy the argument but store a reference if it is a reference_wrapper. Useful if that argument (or the functor!) need not be copied but stays in scope while the bind-functor is used.

Difference from ordinary pointers

  • There is no additional level of syntactical indirection. Pointers have to be dereferenced to obtain an lvalue to the object they refer to; reference_wrappers have an implicit conversion operator and can be called like the object they wrap.

    int i;
    int& ref = std::ref(i); // Okay
  • reference_wrappers, unlike pointers, don't have a null state. They have to be initialized with either a reference or another reference_wrapper.

    std::reference_wrapper<int> r; // Invalid
  • A similarity are the shallow copy semantics: Pointers and reference_wrappers can be reassigned.

C++ Difference between std::ref(T) and T&?

Well ref constructs an object of the appropriate reference_wrapper type to hold a reference to an object. Which means when you apply:

auto r = ref(x);

This returns a reference_wrapper and not a direct reference to x (ie T&). This reference_wrapper (ie r) instead holds T&.

A reference_wrapper is very useful when you want to emulate a reference of an object which can be copied (it is both copy-constructible and copy-assignable).

In C++, once you create a reference (say y) to an object (say x), then y and x share the same base address. Furthermore, y cannot refer to any other object. Also you cannot create an array of references ie code like this will throw an error:

#include <iostream>
using namespace std;

int main()
{
int x=5, y=7, z=8;
int& arr[] {x,y,z}; // error: declaration of 'arr' as array of references
return 0;
}

However this is legal:

#include <iostream>
#include <functional> // for reference_wrapper
using namespace std;

int main()
{
int x=5, y=7, z=8;
reference_wrapper<int> arr[] {x,y,z};
for (auto a: arr)
cout << a << " ";
return 0;
}
/* OUTPUT:
5 7 8
*/

Talking about your problem with cout << is_same<T&,decltype(r)>::value;, the solution is:

cout << is_same<T&,decltype(r.get())>::value;  // will yield true

Let me show you a program:

#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;

int main()
{
cout << boolalpha;
int x=5, y=7;
reference_wrapper<int> r=x; // or auto r = ref(x);
cout << is_same<int&, decltype(r.get())>::value << "\n";
cout << (&x==&r.get()) << "\n";
r=y;
cout << (&y==&r.get()) << "\n";
r.get()=70;
cout << y;
return 0;
}
/* Ouput:
true
true
true
70
*/

See here we get to know three things:

  1. A reference_wrapper object (here r) can be used to create an array of references which was not possible with T&.

  2. r actually acts like a real reference (see how r.get()=70 changed the value of y).

  3. r is not same as T& but r.get() is. This means that r holds T& ie as its name suggests is a wrapper around a reference T&.

I hope this answer is more than enough to explain your doubts.

How can a std::reference_wrapper to a rvalue lambda work?

OK, let's start with the basics: the above code is certainly not legal because it is ill-formed in some rather basic ways. The line

std::function<bool(int)> f(std::cref([n](int i) {return i%n == 0));

would at bare minimum need to be written as

std::function<bool(int)> f(std::cref([n](int i) {return i%n == 0;}));

Note that the code was written in the Dr.Dobb's article as it was in the question, i.e., any statement of the code being legal is already quite questionable.

Once the simple syntax errors are resolved the next question is whether std::cref() can actually be used to bind to an rvalue. The lambda exrpession is clearly a temporary according to 5.1.2 [expr.prim.lambda] paragraph 2 (thanks to DyP for the reference). Since it would generally be a rather bad idea to bind a reference to a temporary and is prohibited elsewhere, std::cref() would be a way to circumvent this restriction. It turns out that according to 20.10 [function.objects] paragraph 2 std::cref() is declared as

template <class T> reference_wrapper<const T> cref(const T&) noexcept;
template <class T> void cref(const T&&) = delete;
template <class T> reference_wrapper<const T> cref(reference_wrapper<T>) noexcept;

That is, the statement is incorrect even after correcting the syntax errors. Neither gcc nor clang compile this code (I have used fairly recent versions of both compilers with their respective standard C++ libraries). That is, based on the above declaration this code is clearly illegal!

Finally, there is nothing which would extend the life-time of the temporary in the above expression. The only reason the life-time of a temporary is extended is when it or one of its data members is immediately bound to a [const] reference. Wrapping a function call around the temporary inhibits this life-time extensions.

In summary: the code quoted in the article is not legal on many different levels!

std::thread taking lambda with ref arg fails to compile

Another variation on the same theme that template argument deduction doesn't look through conversions.

The operator() of f<int> is

void operator() (int& result);

when you pass a reference_wrapper<int> to it, the conversion function (operator int &) is called, yielding a reference that can be bound to result.

The operator() of your generic lambda is

template<class T> void operator() (T& result) const;

If it were passed a reference_wrapper lvalue, it would deduce T as a reference_wrapper and then fail to compile on the assignment. (Assignment to a reference_wrapper reseats the "reference" rather than affects the value.)

But it fails even before that, because the standard requires that what you pass to std::thread must be callable with prvalues - and a non-const lvalue reference doesn't bind to a prvalue. This is the error you see - result_of contains no type because your functor is not callable for the argument type. If you attempt to do g(std::ref(x));, clang produces a rather clear error:

main.cpp:16:5: error: no matching function for call to object of type '(lambda at main.cpp:11:14)'
g(std::ref(x));
^
main.cpp:11:14: note: candidate function [with $auto-0-0 = std::__1::reference_wrapper<int>] not viable: expects an l-value for 1st argument
auto g = [](auto& result) { result = 1; };
^

You should probably consider just capturing the relevant local by reference:

auto g = [&x]() { x = 1; };

Or if, for whatever reason, you must use a generic lambda, then you might take a reference_wrapper by value (or by const reference), and then unwrap it using get():

 auto g = [](auto result) { result.get() = 1; };

or perhaps add a std::bind which will unwrap the reference_wrappers, which lets template argument deduction do the right thing (hat tip @Casey):

 std::thread(std::bind(g, std::ref(x)));

or perhaps dispense with this reference_wrapper nonsense and write your lambda to take a non-owning pointer instead:

auto g = [](auto* result) { *result = 1; };
std::thread(g, &x);


Related Topics



Leave a reply



Submit