2015-07-07 13 views
5

Ich möchte ein Makro erstellen, das ein Paar in zwei lokale Variablen entpackt. Ich möchte nicht eine Kopie des Paares erstellen, wenn es nur eine Variable ist, was würde dies erreichen:Wie kann ich ein Makro erstellen, das einen Wert mehrfach verwendet, ohne es zu kopieren?

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

UNPACK_PAIR(one, two, x); 

Allerdings würde Ich mag es auch nicht um den Ausdruck auszuwerten es mehrfach gegeben hat, z.B. dies sollte expensive_computation() nur einmal nennen:

UNPACK_PAIR(one, two, expensive_computation()); 

Wenn ich tun:

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

dann funktioniert es für den expensive_computation() Fall, aber es macht eine Kopie im x Fall. Wenn ich das tue:

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

Dann funktioniert es im x Fall ohne eine Kopie zu machen, aber nicht in dem expensive_computation() Fall. Wenn ich das tue:

Diese beiden kompilieren und ausführen, aber ich vermute, dass sie undefiniertes Verhalten aufrufen - bin ich das richtig? Würden beide davon einen Sinn ergeben?

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

Gibt es eine Möglichkeit, einen Makro zu erstellen, die für diese beiden Anwendungsfälle funktionieren - nicht x Kopieren aber auch nicht undefiniertes Verhalten aufrufen, wenn das Ergebnis eines Ausdruck oder Funktionsaufruf gegeben?

+0

* „? Diese beide kompilieren und ausführen, aber ich vermute, dass sie nicht definiertes Verhalten aufrufen - bin ich über die richtigen“ * Dieser von der Nutzung abhängt. Es ruft UB für so etwas auf wie 'auto id = [] (auto & & x) -> decltype (automatisch) {return decltype (x) (x); }; auto && tmp = id (5); ', aber es ruft UB nicht für' auto && tmp = 5; 'auf - dies hat etwas mit der Verlängerung der Lebenszeit von Provisorien zu tun, die an die Referenz gebunden sind. Müssen Sie ** den Wert "V" über Ihr Makro am Leben halten? 'auto tmp = V;' behält auch seinen Wert bei, es sei denn, es hat ein internes Lebensdauerproblem. – dyp

+0

@dyp: Ja, ich möchte den Wert am Leben erhalten, ohne eine Kopie davon zu machen (was 'auto tmp = V' tun würde, wenn eine lokale Variable gegeben würde) – Claudiu

+0

[This] (http://stackoverflow.com/questions/9431487/cc-define-macro-inside-macro) könnte relevant sein. –

Antwort

4

auto&& erstellt eine Weiterleitungsreferenz, d. H. Es akzeptiert alles. Es tut nicht (immer) erstellen Sie eine Rvalue-Referenz. Also dies nur tun:

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

Allerdings würde ich stark gegen diese vorschlagen (es sei denn, der Umfang der Verwendung von UNPACK_PAIR sehr begrenzt ist und der Betrieb ist wirklich allgegenwärtig in diesem Umfang). Es sieht aus wie Dunkelheit für keinen wirklichen Nutzen für mich. Stellen Sie sich vor, nach 6 Monaten mit nur zwei Stunden zum Projekt zurückzukehren, um einen kritischen Fehler zu finden. Würden Sie sich dafür bedanken, dass Sie eine nicht standardmäßige makrobasierte Syntax anstelle von etwas Lesbarem verwenden?

+0

Beachten Sie, dass dies lebenslange Probleme in bestimmten Fällen verursachen kann (wenn "PAIR" ein Ausdruck ist, der ein temporäres Objekt zurückgibt, das nicht direkt an "tmp" bindet). – dyp

+0

@dyp Können Sie ein Beispiel beschreiben, wenn so etwas passieren würde? – Angew

+0

Siehe meinen Kommentar zum OP: 'auto id = [] (auto & & x) -> decltype (automatisch) {return decltype (x) (x); }; auto && tmp = id (5); '. Ja, das ist pathologisch, aber es kann vorkommen. – dyp

6

Sie brauchen dafür kein Makro.

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

Wenn Sie Verweise auf bestehende Mitglieder eines Paares:

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

Das ist es.

Jetzt können Sie etwas anspruchsvoller/interessant/wichtig weitergehen.

+2

Es sei denn, der Typ ist nicht standardmäßig konstruierbar. – dyp

+0

Ich würde gerne. Anstelle von 'type_of_first x; type_of_second y; std :: tie (x, y) = p; 'Ich möchte 'UNPACK_PAIR (x, y, p)' machen. Beachten Sie, wie viel einfacher das Makro ist, und ich muss keine Typen deklarieren. – Claudiu

+0

@Claudiu: Nur zu sagen "Ich würde gerne tun, UNPACK_PAIR (x, y, p)" "ist völlig sinnlos, wenn weder Sie noch wir eine Ahnung haben, was" UNPACK_PAIR "tun würde. Nennen Sie Ihre tatsächlichen Anforderungen und Einschränkungen. –

2

Was Sie wollen, ist std::tie.

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

Wenn Sie möchten, können Sie sogar das verwenden, um Ihr Makro zu definieren. Beachten Sie, dass dies nur für 2-Tupel funktioniert - wenn Sie 3-Tupel oder mehr möchten, müssen Sie dies etwas anders machen.Zum Beispiel, wenn Sie eine 3-Tupel 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

Dies kopiert jedoch die Werte (vorausgesetzt, das Paar speichert keine Referenzen). Und es funktioniert unnötigerweise nicht mit nicht standardmäßig konstruierbaren Typen. 'declltype (p.first) & x = p.first;' etc macht mehr Sinn IMO, aber das ist sehr nah an der (besseren) 'auto & x = p.first;' von anderen vorgeschlagen. – dyp

+0

Ja, der einzige Grund, 'declltype' hier zu verwenden, wäre, dass Sie ihn nicht sofort initialisieren müssen. Ansonsten ist "auto" überlegen. – celticminstrel