Reputation: 229451
I'd like to create a macro which unpacks a pair into two local variables. I'd like to not create a copy of the pair if it's just a variable, which this would accomplish:
#define UNPACK_PAIR(V1, V2, PAIR) \
auto& V1 = PAIR.first; \
auto& V2 = PAIR.second;
UNPACK_PAIR(one, two, x);
However, I'd also like it to not evaluate the expression it's given multiple times, e.g. this should only call expensive_computation()
once:
UNPACK_PAIR(one, two, expensive_computation());
If I do:
#define UNPACK_PAIR_A(V1, V2, PAIR) \
auto tmp = PAIR; \
auto& V1 = tmp.first; \
auto& V2 = tmp.second;
then it works for the expensive_computation()
case, but it makes a copy in the x
case. If I do:
#define UNPACK_PAIR_R(V1, V2, PAIR) \
auto& tmp = PAIR; \
auto& V1 = tmp.first; \
auto& V2 = tmp.second;
Then it works in the x
case without making a copy but fails in the expensive_computation()
case. If I do:
#define UNPACK_PAIR_CR(V1, V2, PAIR) \
const auto& tmp = PAIR; \
auto& V1 = tmp.first; \
auto& V2 = tmp.second;
#define UNPACK_PAIR_RR(V1, V2, PAIR) \
auto&& tmp = PAIR; \
auto& V1 = tmp.first; \
auto& V2 = tmp.second;
These both compile and run, but I suspect they invoke undefined behavior - am I correct about that? Also, would either of these make any sense?
#define UNPACK_PAIR_RR(V1, V2, PAIR) \
auto&& tmp = std::move(PAIR); \
auto& V1 = tmp.first; \
auto& V2 = tmp.second;
#define UNPACK_PAIR_RR(V1, V2, PAIR) \
auto&& tmp = std::forward<decltype(PAIR)>(PAIR); \
auto& V1 = tmp.first; \
auto& V2 = tmp.second;
Is there any way to create a macro that works for both of these use cases - not copying x
yet also not invoking undefined behavior when given the result of an expression or function call?
Upvotes: 6
Views: 450
Reputation: 1679
What you want is std::tie
.
decltype(p.first) x;
decltype(p.second) y;
std::tie(x,y) = p;
If you want, you could even use that to define your macro. Note that this will only work for 2-tuples - if you want 3-tuples or more, you'll need to do it a bit differently. For example, if you have a 3-tuple t
:
decltype(std::get<0>(t)) x;
decltype(std::get<1>(t)) y;
decltype(std::get<2>(t)) z;
std::tie(x,y,z) = t;
Upvotes: 2
Reputation: 171167
auto&&
creates a forwarding reference, i.e. it accepts anything. It does not (always) create an rvalue reference. So just do this:
#define UNPACK_PAIR(V1, V2, PAIR) \
auto&& tmp = PAIR; \
auto& V1 = tmp.first; \
auto& V2 = tmp.second;
However, I would strongly suggest against this (unless the scope of the use of UNPACK_PAIR
is very limited and the operation is really ubiquitous in that scope). It looks like obscurity for no real benefit to me. Imagine returning to the project after 6 months, with just two hours to find a critical bug. Will you be thanking yourself for using a nonstandard macro-based syntax instead of something readable?
Upvotes: 4
Reputation: 385254
You don't need a macro for this.
auto p = std::make_pair(2, 3);
int x, y;
std::tie(x, y) = p;
If you want references to existing members of a pair:
auto p = std::make_pair(2, 3);
auto& x = p.first;
auto& y = p.second;
That's it.
Now you can move on to something more challenging/interesting/important.
Upvotes: 6