Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::tie fails with "cannot bind non-const lvalue reference" when passed value from a function call

I expected this code to work, but it does not compile:

#include <tuple>

struct S
{
    int x = 0;
    int y() const { return 1; }
};

bool f(const S& a, const S& b)
{
    return std::tie(a.x, a.y()) < std::tie(b.x, b.y());
}

GCC 9 says:

error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'

return std::tie(a.x, a.y()) < std::tie(b.x, b.y());
                     ~~~^~

What's wrong with the code, how can it be fixed, and why? I'm trying to write a concise comparison function, and usually std::tie supports that (indeed this is the textbook use case for std::tie).

Demo: https://godbolt.org/z/cWbQC0

like image 356
John Zwinck Avatar asked Jul 04 '19 09:07

John Zwinck


2 Answers

std::tie always expects lvalues for arguments, since its intended purpose is to be used in assignment. To handle other value categories, one may use std::forward_as_tuple:

bool f(const S& a, const S& b)
{
    return std::forward_as_tuple(a.x, a.y()) < std::forward_as_tuple(b.x, b.y());
}

The two tuples now contain rvalue references that are bound to the results of the calls to S::y. Goes without saying that one best be careful with object lifetimes when using it.

like image 175
StoryTeller - Unslander Monica Avatar answered Nov 17 '22 00:11

StoryTeller - Unslander Monica


std::tie takes lvalue references as arguments, so int returned by S::y can't bind. You could use the perfect forwarding version, std::forward_as_tuple, instead:

#include <tuple>

struct S
{
    int x = 0;
    int y() const { return 1; }
};

bool f(const S& a, const S& b)
{
    return std::forward_as_tuple(a.x, a.y()) < std::forward_as_tuple(b.x, b.y());
}

Demo.

like image 6
jrok Avatar answered Nov 17 '22 00:11

jrok