Как я могу создать макрос, который использует значение несколько раз, не копируя его?

Я хотел бы создать макрос, который распаковывает пару в две локальные переменные. Я бы не хотел создавать копию пары, если это всего лишь переменная, чего бы добиться:

#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 но также не вызывает неопределенное поведение, если дан результат выражения или вызова функции?

5

Решение

auto&& создает ссылку для пересылки, то есть принимает все. Оно делает не (всегда) создать ссылку на значение. Так что просто сделайте это:

#define UNPACK_PAIR(V1, V2, PAIR) \
auto&& tmp = PAIR; \
auto& V1 = tmp.first; \
auto& V2 = tmp.second;

Тем не менее, я настоятельно рекомендую против этого (если сфера использования UNPACK_PAIR очень ограничен, и операция действительно вездесущий в этом объеме). Это похоже на безвестность без реальной выгоды для меня. Представьте, что вы вернетесь к проекту через 6 месяцев, и у вас будет всего два часа, чтобы найти критическую ошибку. Будете ли вы благодарить себя за использование нестандартного синтаксиса на основе макросов вместо чего-то удобочитаемого?

4

Другие решения

Вам не нужен макрос для этого.

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;

Вот и все.

Теперь вы можете перейти к чему-то более сложному / интересному / важному.

6

Что вы хотите 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;
2
По вопросам рекламы [email protected]