The following code
#include <functional>
#include <tuple>
#include <iostream>
struct Point
{
int x;
int y;
};
decltype(auto) memrefs(Point &p)
{
return std::make_tuple(std::ref(p.x), std::ref(p.y));
}
int main()
{
Point p;
auto& [x, y] = memrefs(p);
x = 1;
y = 2;
std::cout << p.x << " " << p.y << std::endl;
return 0;
}
does not compile. Reported by GCC 8.1.0:
error: cannot bind non-const lvalue reference of type 'std::tuple<int&, int&>&' to an rvalue of type 'std::tuple<int&, int&>'
However, it will compile if I change
auto& [x, y] = memrefs(p);
to
auto [x, y] = memrefs(p)
My question is why? Aren't x
and y
references?
CodePudding user response:
decltype(auto) memrefs(Point &p);
is
std::tuple<std::reference_wrapper<int>, std::reference_wrapper<int>> memrefs(Point &p);
In structured_binding,
auto&
in auto& [x, y]
applies to the "tuple", not to x
, y
.
and you cannot have
std::tuple<std::reference_wrapper<int>, std::reference_wrapper<int>>& tup = memrefs(p);
but you can have
std::tuple<std::reference_wrapper<int>, std::reference_wrapper<int>> tup = memrefs(p);
then x
, y
refers to the appropriate part of the tuple.
CodePudding user response:
The line
return std::make_tuple(std::ref(p.x), std::ref(p.y));
is constructing a temporary local variable of type std::tuple<int&, int&>
and returns it as a rvalue. In this case, being a temporary variable, it can be acquired with a const reference (const std::tuple<int&, int&>&
) or it can be copied into a new tuple (std::tuple<int&, int&>
). In your case you are trying to acquire a non const reference (std::tuple <int&, int&>&
) to a temporary object and this is not allowed.
How you pointed out, the solution is to copy the value returned by memrefs
in a new value or to acquire it using a const reference.