Reputation: 13
I have a struct that contains two weak pointers, and I wish to compare this struct lexicographically using std::tie. However, I ran across a strange issue where I realized that I can't use std::weak_ptr::lock() as an argument of std::tie. Example code:
struct S {
S(std::shared_ptr<int>& f, std::shared_ptr<int>& s) {
first = f;
second = s;
}
bool operator<(const S& rhs) const {
return std::tie(first.lock(), second.lock()) < std::tie(rhs.first.lock(), rhs.second.lock());
}
std::weak_ptr<int> first, second;
};
Doing this results in a compilation error with a code of E0304
: no instance of function template "std::tie" matches the argument list
.
However, if I create new std::shared_ptr
objects and set them to the values of std::weak_ptr::lock() and then compare them, everything works just fine:
struct S {
S(std::shared_ptr<int>& f, std::shared_ptr<int>& s) {
first = f;
second = s;
}
bool operator<(const S& rhs) const {
// creating shared_ptrs and assigning them the value of std::weak_ptr::lock()
std::shared_ptr<int>
f = first.lock(), s = second.lock(),
rf = rhs.first.lock(), rs = rhs.second.lock();
// compare those shared_ptrs
return std::tie(f, s) < std::tie(rf, rs);
}
std::weak_ptr<int> first, second;
};
int main() {
std::shared_ptr<int>
a(new int(10)),
b(new int(5));
// just two S initializations
S foo(a, b);
S bar(b, a);
if (foo < bar) {
std::cout << "Foo is less than Bar";
}
else {
std::cout << "Otherwise";
}
}
Output: Foo is less than Bar
Any reason as to why this may be? Thank you!
Upvotes: 0
Views: 138
Reputation: 72271
std::tie
is for lvalues only, creating return types such as std::tuple<T1&, T2&>
. The lock()
member function returns a shared_ptr
by value, so the expression is an rvalue and cannot be used with std::tie
.
You can use the same tuple lexicographical trick more generally by writing std::forward_as_tuple
instead:
return std::forward_as_tuple(first.lock(), second.lock()) <
std::forward_as_tuple(rhs.first.lock(), rhs.second.lock());
The return type of these forward_as_tuple
calls will be std::tuple<std::shared_ptr<int>&&, std::shared_ptr<int>&&>
. forward_as_tuple
is a bit more dangerous than tie
in general since it can in some uses create dangling references with no compiler warnings, but it's safe here since the tuple
objects are temporary and will not be used after the operator<
evaluation.
Upvotes: 2
Reputation: 59952
std::tie
takes in arguments by reference since it builds a tuple of references from them. Temporaries, like the std::shared_ptr
returned by lock()
, cannot be bound to references. Creating separate variables gives the references something to bind to.
Upvotes: 3