Home > Software engineering >  Confusion on return type deduction with unpacking references
Confusion on return type deduction with unpacking references

Time:12-20

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.

  • Related