2015-11-02 15 views
7

Ich habe dieses Beispiel:Warum wurde der Konstruktor std :: string nicht aufgerufen?

#include <string> 
#include <iostream> 

class Test { 
private: 
    std::string str; 
public: 
    Test(std::string &&str_) : 
     str(str_) 
    {} 

    const std::string &GetStr() 
    { 
     return str; 
    } 
}; 

int main(int argc, char *argv[]) 
{ 
    std::string there("1234567890"); 
    std::cout << "1. there: " << there << '\n'; 

    Test t1(std::move(there)); 

    std::cout << "2. there: " << there << '\n'; 
    std::cout << "3. there: " << t1.GetStr() << '\n'; 
} 

Es gibt den Ausgang

$ ./a.out 
1. there: 1234567890 
2. there: 1234567890 
3. there: 1234567890 

Dies wird gcc 5.1.1 auf Linux verwenden. Während die there-Zeichenfolge nach der Verschiebung in einem gültigen, aber unbestimmten Zustand verbleibt, scheint diese Implementierung den String zu verschieben (und nicht zu kopieren), wenn der std :: string-Move-Konstruktor aufgerufen wird.

wenn ich die initalizer str(str_) mit str(std::move(str_)) ersetzen erhalte ich diese Ausgabe:

$ ./a.out 
1. there: 1234567890 
2. there: 
3. there: 1234567890 

Dies legt nahe, die std :: string bewegen Konstruktor jetzt verwendet wird, aber warum std::string(std::string &&) ist nicht in meinem ersten Beispiel aufgerufen?

Antwort

6

Sie

public: 
    Test(std::string &&str_) : 
     str(std::move(str_)) 
    {} 

str_ tun haben einen Namen machen sollen, ist ein benanntes Objekt, so dass es nicht auf eine Funktion als R-Wert-Referenz übergeben werden.

Eine vom Standard-Komitee getroffene Design-Auswahl verhindert, dass es als R-Wert behandelt wird, sodass Sie es nicht versehentlich ändern lassen können. Insbesondere: Der Typ str_ do ist eine lvalue-Referenz auf string, aber str_ wird nicht als rvalue betrachtet, da es ein benanntes Objekt ist.

Sie müssen Ihre Absicht explizit machen, indem Sie einen Anruf zu std::move hinzufügen. Wenn Sie dies tun, geben Sie an, dass Sie str_ als R-Wert definieren möchten und Sie alle Konsequenzen dieser Wahl kennen.

3

Weil Lvalue-Referenz immer gewinnt! Deshalb müssen Sie explizit std::move angeben.

Es ist gestattet, Verweise auf Referenzen durch Art Manipulationen in Vorlagen oder typedefs zu bilden, in der Bezug Fall Kollabieren Regeln gelten: rvalue Bezug auf rvalue Referenz zu rvalue Referenz kollabiert, alle anderen Kombinationen bilden lvalue Referenz :

typedef int& lref; 
typedef int&& rref; 
int n; 
lref& r1 = n; // type of r1 is int& 
lref&& r2 = n; // type of r2 is int& 
rref& r3 = n; // type of r3 is int& 
rref&& r4 = 1; // type of r4 is int&& 

von here genommen.

Verwandte Themen