Consider I have a custom type (which I can extend):
struct Foo {
int a;
string b;
};
How can I make an instance of this object assignable to a std::tie
, i.e. std::tuple
of references?
Foo foo = ...;
int a;
string b;
std::tie(a, b) = foo;
Failed attempts:
Overloading the assignment operator for tuple<int&,string&> = Foo
is not possible, since assignment operator is one of the binary operators which have to be members of the left hand side object.
So I tried to solve this by implementing a suitable tuple-conversion operator. The following versions fail:
operator tuple<int,string>() const
operator tuple<const int&,const string&>() const
They result in an error at the assignment, telling that "operator =
is not overloaded for tuple<int&,string&> = Foo
". I guess this is because "conversion to any type X + deducing template parameter X for operator=" don't work together, only one of them at once.
Imperfect attempt:
Hence I tried to implement a conversion operator for the exact type of the tie:
operator tuple<int&,string&>() const
Demo
operator tuple<int&,string&>()
Demo
The assignment now works since types are now (after conversion) exactly the same, but this won't work for three scenarios which I'd like to support:
- If the tie has variables of different but convertible types bound (i.e. change
int a;
to long long a;
on the client side), it fails since the types have to fully match. This contradicts the usual use of assigning a tuple to a tuple of references which allows convertible types.(1)
- The conversion operator needs to return a tie which has to be given lvalue references. This won't work for temporary values or const members.(2)
- If the conversion operator is not const, the assignment also fails for a
const Foo
on the right hand side. To implement a const version of the conversion, we need to hack away const-ness of the members of the const subject. This is ugly and might be abused, resulting in undefined behavior.
I only see an alternative in providing my own tie
function + class together with my "tie-able" objects, which makes me force to duplicate the functionality of std::tie
which I don't like (not that I find it difficult to do so, but it feels wrong to have to do it).
I think at the end of the day, the conclusion is that this is one drawback of a library-only tuple implementation. They're not as magic as we'd like them to be.
EDIT:
As it turns out, there doesn't seem to be a real solution addressing all of the above problems. A very good answer would explain why this isn't solvable. In particular, I'd like someone to shed some light on why the "failed attempts" can't possibly work.
(1): A horrible hack is to write the conversion as a template and convert to the requested member types in the conversion operator. It's a horrible hack because I don't know where to store these converted members. In this demo I use static variables, but this is not thread-reentrant.
(2): Same hack as in (1) can be applied.
See Question&Answers more detail:
os