Я хотел бы создать макрос, который распаковывает пару в две локальные переменные. Я бы не хотел создавать копию пары, если это всего лишь переменная, чего бы добиться:
#define UNPACK_PAIR(V1, V2, PAIR) \
auto& V1 = PAIR.first; \
auto& V2 = PAIR.second;
UNPACK_PAIR(one, two, x);
Однако я также хотел бы, чтобы он не оценивал выражение, которое он давал несколько раз, например это должен только позвонить expensive_computation()
один раз:
UNPACK_PAIR(one, two, expensive_computation());
Если я сделаю:
#define UNPACK_PAIR_A(V1, V2, PAIR) \
auto tmp = PAIR; \
auto& V1 = tmp.first; \
auto& V2 = tmp.second;
тогда это работает для expensive_computation()
случае, но это делает копию в x
дело. Если я сделаю:
#define UNPACK_PAIR_R(V1, V2, PAIR) \
auto& tmp = PAIR; \
auto& V1 = tmp.first; \
auto& V2 = tmp.second;
Тогда это работает в x
случае, не делая копию, но терпит неудачу в expensive_computation()
дело. Если я сделаю:
#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;
Они оба компилируются и запускаются, но я подозреваю, что они вызывают неопределенное поведение — я прав? Кроме того, имеет ли какой-либо из них смысл?
#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;
Есть ли способ создать макрос, который работает для обоих этих вариантов использования — не копирование x
но также не вызывает неопределенное поведение, если дан результат выражения или вызова функции?
auto&&
создает ссылку для пересылки, то есть принимает все. Оно делает не (всегда) создать ссылку на значение. Так что просто сделайте это:
#define UNPACK_PAIR(V1, V2, PAIR) \
auto&& tmp = PAIR; \
auto& V1 = tmp.first; \
auto& V2 = tmp.second;
Тем не менее, я настоятельно рекомендую против этого (если сфера использования UNPACK_PAIR
очень ограничен, и операция действительно вездесущий в этом объеме). Это похоже на безвестность без реальной выгоды для меня. Представьте, что вы вернетесь к проекту через 6 месяцев, и у вас будет всего два часа, чтобы найти критическую ошибку. Будете ли вы благодарить себя за использование нестандартного синтаксиса на основе макросов вместо чего-то удобочитаемого?
Вам не нужен макрос для этого.
auto p = std::make_pair(2, 3);
int x, y;
std::tie(x, y) = p;
Если вы хотите ссылки на существующих членов пары:
auto p = std::make_pair(2, 3);
auto& x = p.first;
auto& y = p.second;
Вот и все.
Теперь вы можете перейти к чему-то более сложному / интересному / важному.
Что вы хотите std::tie
,
decltype(p.first) x;
decltype(p.second) y;
std::tie(x,y) = p;
Если вы хотите, вы можете даже использовать это, чтобы определить свой макрос. Обратите внимание, что это будет работать только для 2-кортежей — если вы хотите 3-кортеж или более, вам нужно будет сделать это немного по-другому. Например, если у вас есть 3-кортеж 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;