2015-07-07 7 views
5

Vorrei creare una macro che decomprimi una coppia in due variabili locali. Mi piacerebbe non creare una copia della coppia se è solo una variabile, che quest'anno avrebbe compiuto:Come posso creare una macro che usa un valore più volte, senza copiarlo?

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

UNPACK_PAIR(one, two, x); 

Tuttavia, mi piacerebbe anche di non valutare l'espressione è dato più volte, ad esempio, questo dovrebbe solo chiamare expensive_computation() volta:

UNPACK_PAIR(one, two, expensive_computation()); 

Se lo faccio:

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

allora funziona per il caso expensive_computation(), ma rende una copia nel caso x. Se faccio:

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

allora funziona nel caso x senza fare una copia, ma non riesce nel caso expensive_computation(). Se faccio:

#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; 

Questi sia compilare ed eseguire, ma ho il sospetto che invocano un comportamento indefinito - Ho ragione a tale proposito? Inoltre, uno di questi avrebbe senso?

#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; 

Esiste un modo per creare una macro che funziona per entrambi i casi d'uso - non copiare x ancora, inoltre, non invocando un comportamento indefinito quando dato il risultato di una chiamata di un'espressione o una funzione?

+0

* "? Questi sia compilare ed eseguire, ma ho il sospetto che invocano un comportamento indefinito - Ho ragione di quel" * Questo dipende dall'uso. Invoca UB per qualcosa come 'auto id = [] (auto && x) -> decltype (auto) {return decltype (x) (x); }; auto && tmp = id (5); ', ma non invoca UB per' auto && tmp = 5; '- questo ha a che fare con l'estensione della durata dei temporaries legati al riferimento. Hai ** bisogno ** di mantenere il valore 'V' vivo tramite la tua macro? 'auto tmp = V;' mantiene anche il suo valore in vita, a meno che non abbia un problema di durata interna. – dyp

+0

@dyp: Sì, mi piacerebbe mantenere il valore in vita, senza doverne fare una copia (cosa che 'auto tmp = V' farebbe se avesse una variabile locale) – Claudiu

+0

[This] (http://stackoverflow.com)/questions/9431487/cc-define-macro-inside-macro) potrebbe essere rilevante. –

risposta

4

auto&& crea un riferimento di inoltro, vale a dire accetta qualsiasi cosa. Fa non (sempre) crea un riferimento di rvalue. Quindi, solo fare questo:

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

Tuttavia, vi consiglierei vivamente contro questo (a meno che il campo di applicazione della uso di UNPACK_PAIR è molto limitato ed il funzionamento è davvero onnipresente in tale ambito). Sembra un'oscurità senza alcun beneficio reale per me. Immagina di tornare al progetto dopo 6 mesi, con solo due ore per trovare un bug critico. Ti ringrazerai per l'utilizzo di una sintassi non standard basata su macro invece di qualcosa di leggibile?

+0

Si noti che questo può causare problemi di durata per alcuni casi d'angolo (se 'PAIR' è un'espressione che restituisce un valore temporaneo che non si collega direttamente a' tmp'). – dyp

+0

@dyp Puoi descrivere un esempio in cui una cosa del genere potrebbe accadere? – Angew

+0

Vedere il mio commento all'OP: 'id auto = [] (auto && x) -> decltype (auto) {return decltype (x) (x); }; auto && tmp = id (5); '. Sì, è patologico, ma può accadere. – dyp

6

Non è necessaria una macro per questo.

auto p = std::make_pair(2, 3); 
int x, y; 
std::tie(x, y) = p; 

Se si desidera riferimenti a membri esistenti di una coppia:

auto p = std::make_pair(2, 3); 
auto& x = p.first; 
auto& y = p.second; 

Questo è tutto.

Ora puoi passare a qualcosa di più impegnativo/interessante/importante.

+2

A meno che il tipo non sia predefinito-costruibile. – dyp

+0

Mi piacerebbe. Invece di 'type_of_first x; type_of_second y; std :: tie (x, y) = p; 'Mi piacerebbe fare 'UNPACK_PAIR (x, y, p)'. Nota quanto è più semplice la macro e non devo dichiarare alcun tipo. – Claudiu

+0

@Claudiu: Il solo dire "Mi piacerebbe fare" UNPACK_PAIR (x, y, p) '" è completamente inutile quando né tu né noi abbiamo idea di cosa farebbe UNPACK_PAIR. Indica i tuoi requisiti e i tuoi limiti effettivi. –

2

Quello che desideri è std::tie.

decltype(p.first) x; 
decltype(p.second) y; 
std::tie(x,y) = p; 

Se lo desideri, puoi anche usarlo per definire la tua macro. Nota che questo funzionerà solo con le tuple da 2, se vuoi le tuple da 3 o più, dovrai farlo un po 'diversamente.Ad esempio, se si dispone di un 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; 
+1

Questo copierà comunque i valori (supponendo che la coppia non memorizzi i riferimenti). E inutilmente non funziona con tipi costruibili non predefiniti. 'decltype (p.first) & x = p.first;' etc ha più senso IMO, ma questo è molto vicino al (meglio) 'auto & x = p.first;' proposto da altri. – dyp

+0

Sì, l'unica ragione per usare 'decltype' qui sarebbe quindi non devi immediatamente inizializzarlo. Altrimenti "auto" è superiore. – celticminstrel

Problemi correlati