Brug af std::forward vs std::move

Brug af std::forward vs std::move

Du kan ikke brug std::forward uden eksplicit at angive dets skabelonargument. Det bruges med vilje i en ikke-udledt sammenhæng.

For at forstå dette skal du virkelig forstå, hvordan du videresender referencer (T&& for en udledt T ) arbejde internt, og ikke vifte dem væk som "det er magi." Så lad os se på det.

template <class T>
void foo(T &&t)
{
  bar(std::forward<T>(t));
}

Lad os sige, at vi kalder foo sådan her:

foo(42);
  • 42 er en rværdi af typen int .
  • T er deduceret til int .
  • Opkaldet til bar bruger derfor int som skabelonargument for std::forward .
  • Returtypen std::forward<U> er U && (i dette tilfælde er det int && ) så t videresendes som en rværdi.

Lad os nu ringe til foo sådan her:

int i = 42;
foo(i);
  • i er en lværdi af typen int .
  • På grund af den særlige regel for perfekt videresendelse, når en lværdi af typen V bruges til at udlede T i en parameter af typen T && , V & bruges til fradrag. Derfor, i vores tilfælde, T udledes til at være int & .

Derfor angiver vi int & som skabelonargument til std::forward . Dens returtype vil derfor være "int & && ", som kollapser til int & . Det er en lværdi, så i videresendes som en lværdi.

Oversigt

Hvorfor dette virker med skabeloner er, når du gør std::forward<T> , T er nogle gange en reference (når originalen er en lværdi) og nogle gange ikke (når originalen er en rværdi). std::forward vil derfor kaste til en lvalue eller rvalue reference alt efter hvad der er relevant.

Du kan ikke få dette til at virke i ikke-skabelonversionen, netop fordi du kun har én type tilgængelig. For ikke at nævne det faktum, at setImage(Image&& image) ville slet ikke acceptere lværdier - en lværdi kan ikke binde sig til rværdireferencer.


Jeg anbefaler at læse "Effective Modern C ++" af Scott Meyers , specifikt:

  • Punkt 23 :Forstå std::move og std::forward .
  • Vare 24 :Skelne universelle referencer for rvalue referencer.

rvalue-reference

Denne funktion accepterer rværdier og kan ikke acceptere lværdier.

void ImageView::setImage(Image&& image){
    _image = std::forward(image);        // error 
    _image = std::move(image);           // conventional
    _image = std::forward<Image>(image); // unconventional
}

Bemærk først, at std::move kræver kun et funktionsargument, mens std::forward kræver både et funktionsargument og et skabelontypeargument.

Universelle referencer (videresendelse af referencer)

Denne funktion accepterer alt og udfører perfekt videresendelse.

template <typename T> void ImageView::setImage(T&& image){
    _image = std::forward<T>(image);
}

Du skal angive skabelontypen i std::forward .

I denne sammenhæng Image&& image er altid en r-værdi reference og std::forward<Image> vil altid flytte, så du kan lige så godt bruge std::move .

Din funktion, der accepterer en r-værdi reference, kan ikke acceptere l-værdier, så den svarer ikke til de første to funktioner.