2016-03-25 5 views
16

Więc to zapewnia zamierzony wynik:Jeśli przeniesię obiekt lokalny do funkcji, czy będzie on nadal ważny później?

void f(std::string&& s) 
{ 
    s += " plus extra"; 
} 

int main(void) 
{ 
    std::string str = "A string"; 
    f(std::move(str)); 
    std::cout << str << std::endl; 

    return 0; 
} 

Ciąg plusa extra

Oznacza to, że to działa, gdy uruchomię go na Ideone, ale jest to UB? Dodanie dodatkowych inicjalizacji łańcuchów przed i po wywołaniu do f niczego nie zmieniło.

+2

Tak, nadal będzie ważny, ale zawiera: * niezdefiniowany *. Jedynymi metodami, do których powinieneś wywołać ciąg po * przeniesieniu * z niego, jest zastąpienie jego zawartości czymś nowym. Na przykład 'str.clear();'. – Galik

+1

Powiązane: [Co można zrobić z przeniesionym obiektem?] (Http://stackoverflow.com/q/7027523/3425536) – emlai

+4

Ciąg nie jest przenoszony, referencja rvalue jest wysyłana, a własność nie jest zabrał. Ten kod jest całkowicie poprawny. Nie ma tu udziału UB i nie ma tu żadnej niezdefiniowanej treści _ lub cokolwiek to oznacza. –

Odpowiedz

9

Kod jest ważny, ponieważ nie ma rzeczywistego przeprowadzane jest przenoszenie. Oto w jaki sposób można zrobić to nieważne:

string f(std::string&& s) { 
    std::string res(std::move(s)); 
    res += " plus extra"; 
    return res; 
} 

Stan str po tej rozmowy będzie ważny, ale nieokreślone. Oznacza to, że nadal można przypisać nową wartość do str, aby przywrócić ją do prawidłowego stanu, ale nie można jej wypisać bez wywołania nieokreślonego zachowania (demo). Zobacz this Q&A, aby poznać szczegóły na temat stanu przeniesienia.

10

std::move nic nie przenosi. Wskazuje, że obiekt może być "przeniesiony z", przekazując jego argument do wartości rvalue.

Twój kod jest ważny i nie wykonano żadnej operacji przeniesienia.

Można by uzyskać zachowanie jeśli stan str jest nieokreślona po wywołaniu f(), jeśli move-skonstruować kolejny obiekt string z s. Konstruktor ruchu wykonuje rzeczywistą operację przenoszenia.

przykład:

std::vector<std::string> sv; 

void f(std::string&& s) 
{ 
    s += " plus extra"; 
    sv.push_back(std::move(s));   // move s (str) into a new object 
} 

int main(void) 
{ 
    std::string str = "A string"; 
    f(std::move(str));     
    std::cout << str << std::endl;  // output: empty string 
    std::cout << sv.back() << std::endl; // output: "A string plus extra" 

    return 0; 
} 
+0

Może również wspomnieć o elizmie kopiowania i o tym, w jaki sposób '&&' jest w dużej mierze niepotrzebne poza operatorami przenoszenia/przenoszenia zleceń. – Kevin

18

Jest to ważne, nie UB.

Jest to również potwornie zaciemniony kod. std::move(s) to nic innego jak rzucenie na rowności. Sam w sobie nie generuje w żaden sposób żadnego kodu. Jego jedynym celem jest przekształcenie wartości l do wartości runtue, aby kod klienta mógł przeciążyć wyrażenia lvalue/rvalue (w tym przypadku string).

Należy przejść przez lwartości odniesieniem dla tego przypadku:

void f(std::string& s) 
{ 
    s += " plus extra"; 
} 
... 
f(str); 

Lub alternatywnie, przechodzą przez wartości i zwraca nowy ciąg:

std::string f(std::string s) 
{ 
    s += " plus extra"; 
    return s; 
} 
... 
str = f(std::move(str)); 
3

std::move przesyła po prostu Twój obiekt do referencji rvalue. Ponieważ twoja funkcja przyjmuje referencje i po prostu coś z nią robisz, własność nie jest tu pobierana, więc twój ciąg jest nadal w prawidłowym stanie i można go bezpiecznie używać.

Nie polecam używania tego w kodzie, ponieważ jest to mylące, ponieważ wiele osób uznałoby twój ciąg za nieważny, ponieważ przejęcie własności jest podstawowym zastosowaniem odniesienia rvalue, czyli std::move.

Jeśli naprawdę potrzebujesz, aby wywołać tę funkcję w ten sposób, polecam pisania tego:

std::string myString{"a string"}; 

// leave a comment here to explain what you are doing. 
f(static_cast<std::string&&>(myString)); 

Należy jednak pamiętać, że przykładem może być bardzo różny, jeśli funkcja f wziął wartość zamiast odniesienie. W takim przypadku wywołanie go z wartością std::move lub static_cast spowoduje unieważnienie ciągu znaków.

Powiązane problemy