2015-07-07 2 views
5

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

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

+0

* «? Это как скомпилировать и запустить, но я подозреваю, что они вызывают неопределенное поведение - я правильно о том, что» * Это зависит от использования. Он вызывает UB для чего-то вроде «auto id = [] (auto && x) -> decltype (auto) {return decltype (x) (x); }; auto && tmp = id (5); ', но он не вызывает UB для' auto && tmp = 5; '- это связано с расширением времени жизни временных привязок к ссылке. Вам ** нужно **, чтобы сохранить значение 'V' через ваш макрос? 'auto tmp = V;' также сохраняет свое значение в живых, если только у него нет внутренней проблемы с продолжительностью жизни. – dyp

+0

@ dyp: Да, я хотел бы сохранить значение в живых, не делая его копии (что 'auto tmp = V' будет делать, если задана локальная переменная) – Claudiu

+0

[This] (http://stackoverflow.com/questions/9431487/cc-define-macro-inside-macro) может иметь значение. –

ответ

4

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

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

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

+0

Обратите внимание, что это может вызвать проблемы с временем жизни для определенных угловых случаев (если 'PAIR' является выражением, которое возвращает временное, которое не привязывается непосредственно к' tmp'). – dyp

+0

@ dyp Можете ли вы описать пример, когда такое произойдет? – Angew

+0

Смотрите мой комментарий к OP: 'auto id = [] (auto && x) -> decltype (auto) {return decltype (x) (x); }; auto && tmp = id (5); '. Да, это патологично, но это может произойти. – dyp

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; 

Вот и все.

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

+2

Если тип не является конструктивным по умолчанию. – dyp

+0

Хотелось бы. Вместо 'type_of_first x; type_of_second y; std :: tie (x, y) = p; 'Я хотел бы сделать' UNPACK_PAIR (x, y, p) '. Обратите внимание, насколько проще макрос, и мне не нужно объявлять какие-либо типы. – Claudiu

+0

@Claudiu: Просто говорю: «Я бы хотел сделать« UNPACK_PAIR (x, y, p) », совершенно бессмысленно, когда вы и мы не знаем, что сделает UNPACK_PAIR'. Укажите свои фактические требования и ограничения. –

2

Что вы хотите, это 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; 
+1

Это скопирует значения, хотя (предполагая, что пара не хранит ссылки). И он без необходимости не работает с нестандартными по умолчанию типами. 'decltype (p.first) & x = p.first;' etc имеет больше смысла ИМО, но это очень близко к (лучше) 'auto & x = p.first; ', предложенному другими. – dyp

+0

Да, единственная причина использования 'decltype' здесь была бы так, что вам не нужно сразу ее инициализировать. В противном случае 'auto' превосходит. – celticminstrel