2017-05-03 4 views
61

Ich habe std::tie verwendet, ohne viel darüber nachzudenken. Es funktioniert so habe ich nur akzeptiert, dass:Wie funktioniert std :: tie?

auto test() 
{ 
    int a, b; 
    std::tie(a, b) = std::make_tuple(2, 3); 
    // a is now 2, b is now 3 
    return a + b; // 5 
} 

Aber wie funktioniert diese schwarze Magie Arbeit? Wie ändert sich eine temporäre von std::tie Änderung a und b? Ich finde das interessanter, da es eine Bibliotheksfunktion ist, keine Sprachfunktion, also können wir es sicherlich selbst implementieren und verstehen.

Antwort

94

Um das Kernkonzept zu verdeutlichen, reduzieren wir es auf ein einfacheres Beispiel. Obwohl std::tie nützlich für Funktionen ist (ein Tupel von) mehr Werte zurückgegeben werden, können wir es verstehen ganz gut mit nur einem Wert:

int a; 
std::tie(a) = std::make_tuple(24); 
return a; // 24 

Dinge, die wir in wissen müssen, um vorwärts zu gehen:

  • std::tie Konstruiert und gibt ein Tupel von Referenzen zurück.
  • std::tuple<int> und std::tuple<int&> sind 2 völlig verschiedene Klassen, ohne Verbindung zwischen ihnen, andere, dass sie von der gleichen Vorlage generiert wurden, std::tuple.
  • Tupel hat ein operator= ein Tupel von verschiedenen Typen zu akzeptieren (aber gleiche Zahl), wobei jedes Element individuell von cppreference zugeordnet:

    template< class... UTypes > 
    tuple& operator=(const tuple<UTypes...>& other); 
    

    (3) für alle i, ordnet std::get<i>(other) zu std::get<i>(*this) .

Der nächste Schritt ist dieser Funktionen, um loszuwerden, die nur in die Quere kommen, so können wir unseren Code zu dieser Transformation:

int a; 
std::tuple<int&>{a} = std::tuple<int>{24}; 
return a; // 24 

Der nächste Schritt ist, genau zu sehen, was passiert in diesen Strukturen. Dazu erzeuge ich 2 Arten T Substituent für std::tuple<int> und Tr Substituenten std::tuple<int&>, auf das absolute Minimum für unsere Operationen abgespeckte:

struct T { // substituent for std::tuple<int> 
    int x; 
}; 

struct Tr { // substituent for std::tuple<int&> 
    int& xr; 

    auto operator=(const T& other) 
    { 
     // std::get<I>(*this) = std::get<I>(other); 
     xr = other.x; 
    } 
}; 

auto foo() 
{ 
    int a; 
    Tr{a} = T{24}; 

    return a; // 24 
} 

Und schließlich Ich mag die Strukturen alle zusammen, um loszuwerden (na ja, es ist nicht zu 100% entspricht, aber es ist nahe genug für uns, und explizit genug, um es zu ermöglichen):

auto foo() 
{ 
    int a; 

    { // block substituent for temporary variables 

    // Tr{a} 
    int& tr_xr = a; 

    // T{24} 
    int t_x = 24; 

    // = (asignement) 
    tr_xr = t_x; 
    } 

    return a; // 24 
} 

Also im Grunde initialisiert std::tie(a) ein Datenelement Bezug auf a. erstellt ein Datenelement mit dem Wert 24 und die Zuweisung weist der Datenelementreferenz in der ersten Struktur 24 zu. Da dieses Datenelement jedoch eine Referenz ist, die an a gebunden ist, weist es 24a zu.

13

Dies beantwortet Ihre Frage in keiner Weise, aber lassen Sie es mich trotzdem veröffentlichen, weil C++ 17 im Grunde bereit ist (mit Compiler-Unterstützung), also während sich fragen, wie das veraltete Zeug funktioniert, ist es wahrscheinlich wert, wie zu betrachten Die aktuelle und zukünftige Version von C++ funktioniert ebenfalls.

Mit C++ 17 können Sie sehr kratzen std::tie zugunsten der so genannten strukturierte Bindungen. Sie tun das gleiche (gut, nicht das gleiche, aber sie haben den gleichen Nettoeffekt), obwohl Sie weniger Zeichen eingeben müssen, benötigt es keine Bibliotheksunterstützung, und Sie auch haben die Fähigkeit, Referenzen zu nehmen, wenn das ist was du willst.

(Beachten Sie, dass in C++ 17 Konstrukteurs-do Abzug Argument, wird make_tuple hat so etwas überflüssig, auch.)

int a, b; 
std::tie(a, b) = std::make_tuple(2, 3); 

// C++17 
auto [c, d] = std::make_tuple(4, 5); 
auto [e, f] = std::tuple(6, 7); 
std::tuple t(8,9); auto& [g, h] = t; // not possible with std::tie 
+2

Wenn das letzte Zeile kompiliert Ich bin ein bisschen besorgt. Es sieht so aus, als würde man einen Verweis auf ein temporäres Objekt binden, was illegal ist. –

+0

@NirFriedman Erhalten Sie in diesem Fall keine Lebensdauerverlängerung? – Neil

+2

@Neil Es muss entweder eine R-Wert-Referenz oder eine Konst-L-Referenz sein. Sie können keine Lvalue-Referenz an einen Prvalue (temporär) binden. Obwohl dies in MSVC seit Jahren eine "Erweiterung" ist. –

Verwandte Themen