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
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With