2016-03-21 27 views
5

Wydaje się, że to już bardzo istotne pytanie już zadane.std :: move Vs std :: forward

Whats the difference between std::move and std::forward

Ale każda odpowiedź jest inna i ma zastosowanie i mówi nieco różne rzeczy. Tak więc jestem zdezorientowany.

Mam następującą sytuację.

  • skopiować element do pojemnika
    Copy pozycja jest C++ 03, więc rozumiem, że całkiem dobrze.
  • poz Construct do pojemnika
    element konstruktu do pojemnika wierzę wykorzystuje doskonałe przekazywanie poprawnie do przekazania argumentów przez dwóch funkcji do konstruktora T w emplaceBackInternal() (proszę powiedzieć inaczej, jeśli się mylę).
  • Przenieś element do pojemnika
    Mój problem wydaje się być zrozumieniem przeniesienia elementu do kontenera.

Kod:

template<typename T> 
class Container 
{ 
    std::size_t length; 
    T*   buffer; 

public: 
    void push_back(T const& value) 
    { 
     resizeIfRequired(); 
     pushBackInternal(value); 
    } 
    template<typename... Args> 
    void emplace_back(Args&&... args) 
    { 
     resizeIfRequired(); 
     emplaceBackInternal(std::forward<T>(arg)...); 
    } 
    void push_back(T&& value) 
    { 
     resizeIfRequired(); 
     // Is this forward correct or should it be move 
     moveBackInternal(std::forward<T>(value)); 
    } 
private: 
    void pushBackInternal(T const& value) 
    { 
     // Copy construct object into buffer; 
     new (buffer + length) T(value); 
     ++length; 
    } 
    template<typename... Args) 
    void emplaceBackInternal(Args&&... args) 
    { 
     // Construct object into buffer using arguments; 
     new (buffer + length) T(std::forward<T>(args)...); 
     ++length; 
    } 
    void moveBackInternal(T&& value) 
    { 
     // Move construct object into buffer; 
     // Is this forward correct or should it be move 
     new (buffer + length) T(std::forward<T>(value)); 
     ++length; 
    } 
}; 

I obejmują wszystkie trzy tutaj porównać trzy funkcje z odpowiedzi udzielonych w powyższych odpowiedzi. Głównym powodem jest to, że move i construct wygląda tak podobnie, że wydaje się, że powinny być takie same.

Answer @Potatoswatter Score 67

std :: przodu posiada pojedynczy przypadek użycia: rzucić parametr matrycy funkcji

Według tej definicji, że należy przy użyciu std::move wewnątrz push_back(T&& value) i moveBackInternal(T&& value) jako wartość nie jest parametrem szablonu dla funkcji.

Answer @Howard Hinnant Score 38

jeżeli Y jest lwartością odniesienia rezultat będzie lwartością ekspresję. Jeśli Y nie jest wartością odniesienia dla lwartości, wynikiem będzie wartość rvalue (wyrażenie xvalue to be precise).

Wydaje się, że dzięki tej definicji mogę użyć albo std::move lub std::forward.

Answer @Bo Persson Score 11

std :: przodu służy do przekazania parametr dokładnie tak, jak to zostało przekazane do funkcji.

Wygląda na to, że std::forward jest akceptowalny (chociaż jeśli kliknę link w odpowiedzi wszystkie przykłady skorzystają z funkcji szablonowych).

+0

Er ... na pytanie, na które nie odpowiedziano w połączonych odpowiedziach? – Barry

+0

Er ... która odpowiedź jest poprawna !. Każda z nich określa inny wynik. –

+0

Wszystkie są poprawne. Dwa wystarczy powiedzieć, że intencją 'std :: forward' jest przekazywanie referencji, a trzecia dodatkowo poświęca trochę czasu na wyjaśnienie, czym jest' std :: forward'. – Barry

Odpowiedz

5

W tym przypadku:

void push_back(T&& value) 
{ 
    resizeIfRequired(); 
    moveBackInternal(std::forward<T>(value)); // (1)    
    moveBackInternal(std::move(value));  // (2) 

} 

std::forward<T>(value) i std::move(value)identyczną w tym scenariuszu (nie ma znaczenia, między (1) i (2) ... więc użyć (2)).

move jest bezwarunkową obsadą na xvalue. Ta linia daje wyraz typu T&&, który zawsze jest wartością rvalue.

forward to odlewanie warunkowe. Jeśli T jest typem odniesienia lwartości, to uzyskuje wartość l. W przeciwnym razie (jeśli nie jest to typ odniesienia lub typ odniesienia rwartości), daje wartość r. W naszym przypadku T nie jest typem referencyjnym - otrzymujemy więc wartość rurn.

Tak czy inaczej, kończymy w tym samym punkcie - nazywamy moveBackInternal z value rzutowaniem jako rwartą. Po prostu move() jest prostszym sposobem na osiągnięcie tego celu. forward<T>działa, ale nie jest to konieczne.

+0

Czasami tak było, żałuję, że nie byłem w biurze z tobą (lub autorem posta) i mógłbym szczerze porozmawiać z tablicą o lepszej technice. - Jeśli założę, że praca wykonywana zarówno przez 'std :: move' i' std :: forward' jest pracą w czasie kompilacji. Wtedy nie byłoby lepsze rozwiązanie (1), ponieważ sprawia, że ​​kod wygląda identycznie z 'emplace_back()'. Dlaczego potrzebuję dwóch różnych technik, kiedy mogę mieć jedną technikę, która działa we wszystkich sytuacjach? Mniejsze różnice oznaczają łatwiejsze utrzymanie kodu? –

+0

@LokiAstari Wydaje się być oparte głównie na opiniach. Używanie 'move()' jest prostsze, gdy poruszamy się bezwarunkowo. Kod 'push_back()' zawsze się porusza, więc po co używać bardziej skomplikowanych rzutów? Ostatecznie odkryłem, że jest coś fundamentalnie ... off ... z referencjami do przekazywania. Nie wiem, jak można to zrobić lepiej - i na dobre i na złe, to właśnie otrzymaliśmy. – Barry

+0

Dzięki za wszystkie dane wejściowe. Szczerze mówiąc wciąż próbuję sformułować moją opinię i po prostu staram się grać tutaj w diabły. :-) –