Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

przekazywanie przez wartość a przez r-referencję

Ostatnio zmodyfikowano 2020-08-11 00:51
Autor Wiadomość
latajacaryba
Temat założony przez niniejszego użytkownika
przekazywanie przez wartość a przez r-referencję
» 2020-08-10 15:29:44
Witam, jak w temacie, zastanawiam się, która opcja jest lepsza. Pisząc funkcję która ma domyślnie przyjmować r-wartości, którą z poniższych możliwości powinienem wybrać:

C/C++
void f1( T value ) { } //1

void f2( T && rref ) { } //2

int main() {
   
    T obj;
    f1( std::move( obj ) );
   
    T obj2;
    f2( std::move( obj2 ) );
   
}

Szukając na ten temat informacji udało mi się dojść do poniższych wniosków:

1. przy przekazywaniu obiektów, które są niekopiowalne i realizują jedynie semantykę przeniesienia (np. unique_ptr) powinno się przekazywać je przez wartość czyli opcja nr 1

2. przypadek pierwszy daje nam pewność, że wywołanie
f1( std::move( obj ) );
 odbierze własność obiektowi obj, no w każdym razie zostanie wywołany przenoszący konstruktor kopiujący, który przeniesie dane z obj do value. W drugim przypadku wcale tak być nie musi, co zmusza do przyjrzenia się implementacji funkcji, jeśli chcemy się dowiedzieć, czy zaszło przeniesienie.

3. W wielu miejscach, m.in http://thbecker.net/articles​/rvalue_references​/section_03.html autor sugeruje, że powinno się unikać/rzadko korzystać z r-referencji jako argumentu funkcji:
It is true that you can overload any function in this manner, as shown above. But in the overwhelming majority of cases, this kind of overload should occur only for copy constructors and assignment operators, for the purpose of achieving move semantics

Pytanie więc, czy poza tym punktem drugim są jeszcze jakieś przesłanki dla których powinno się wybierać przekazywanie przez wartość? Zauważyłem, że niektóre funkcje składowe np. kontenerów STL przyjmują wartości przez r-referencję, ale jak udało mi się dowiedzieć jest to spowodowane tym, że przekazywanie przez wartość kolidowałoby z przeładowaniem, które przyjmuje const referencję (np. push_back)?

Edit: Oczywiście z tego co rozumiem, w takim właśnie przypadku, czyli:
C/C++
void fun( const T & ); //lub T&
void fun( T && )
no nie ma wyjścia i ta r-referencja musi być?
P-177442
pekfos
» 2020-08-10 19:11:15
przypadek pierwszy daje nam pewność, że wywołanie
f1( std::move( obj ) );
 odbierze własność obiektowi obj, no w każdym razie zostanie wywołany przenoszący konstruktor kopiujący, który przeniesie dane z obj do value. W drugim przypadku wcale tak być nie musi, co zmusza do przyjrzenia się implementacji funkcji, jeśli chcemy się dowiedzieć, czy zaszło przeniesienie.
I co w sumie z tego, że funkcja nie zabierze obiektu? Jak nie wiesz, to musisz założyć że obiekt został zabrany i teraz jest w stanie poprawnym dla destruktora i innych operacji bez warunków wstępnych. Nieprzeniesiony obiekt również pasuje do tego opisu. Wersja przyjmująca wartość z drugiej strony mówi tylko że przeniosłeś obiekt do argumentu funkcji, nie masz żadnej gwarancji że było to do czegoś potrzebne. Chyba nie będziesz sprawdzać wtedy w implementacji funkcji, czy zaszło przeniesienie z argumentu w jakieś inne miejsce? ;)

powinno się unikać/rzadko korzystać z r-referencji jako argumentu funkcji:
Tak. Jak masz argument przez wartość, to możesz go przenieść sobie gdzieś dalej. Jeśli kontekst wywołujący mógł Ci go przenieść, to tym lepiej. Jeśli nie, zajdzie kopiowanie i przeniesiesz sobie kopię. Użycie && wymusza przeniesienie - a przynajmniej porzucenie założeń, że obiekt zawiera dane, więc użycie takiego interfejsu może wymagać robienia niezbyt poręcznej kopii.

C/C++
void fun( const T & ); //lub T&
void fun( T && )
no nie ma wyjścia i ta r-referencja musi być?
Bez && byłoby niejednoznaczne. Nie stawiałbym tu T&, bo to już znaczy zupełnie co innego. Skoro już mowa o szablonach, T&& nie musi wcale oznaczać r-referencji. Gdy T=int&, T&& to wciąż int&. W tych przypadkach używa się std::forward(), zamiast std::move().
P-177443
latajacaryba
Temat założony przez niniejszego użytkownika
» 2020-08-10 20:47:29
Odnośnie forwarding reference to zdaje sobie sprawę, tutaj chodziło mi o jakiekolwiek typ, dlatego użyłem T zamiast np. int, niemniej dziękuję


Jak masz argument przez wartość, to możesz go przenieść sobie gdzieś dalej.
Co masz na myśli? A czy w przypadku r-referencji nie mogę?
C/C++
using T =...;
void f( T && rref ) {
    otherFun( std::move( rref ) );
}
I ostatnie
Nie stawiałbym tu T&, bo to już znaczy zupełnie co innego
W jak sensie byłoby co innego? Znaczy rozumiem różnice między const referencją a referencją, ale jeśli chodzi o niejednoznaczność, to wystąpiłaby ona w obu przypadkach, niewazne czy byłaby funkcja z argumentem T& czy const T&
P-177444
pekfos
» 2020-08-10 21:20:00
Jak masz argument przez wartość, to możesz go przenieść sobie gdzieś dalej.
Co masz na myśli? A czy w przypadku r-referencji nie mogę?
Potraktowałeś implikację jak równoważność, gdzie tu logika..? Eh, przecież sam Ci, w tym samym paragrafie, napisałem że z r-referencji też możesz przenosić. Używanie l-wartości z funkcjami przyjmującymi tylko r-referencje to wtykanie cylindra w kwadratowy otwór. Taki interfejs jest niewygodny w użyciu, bo wszędzie gdzie nie możesz użyć przeniesienia, musisz ręcznie robić kopie do obiektów tymczasowych.
C/C++
#include <string>
void f( std::string && )
{ }

int main()
{
    std::string tekst = "Ala ma kota";
    f( std::string( tekst ) ); // meh.
    f( std::move( tekst ) );
}
Gdy f() w środku przenosi argument dalej, to kopia się nie marnuje. Kopiowanie argumentu domyślnie daje tą bazową wydajność sprzed C++11, gdzie robiło się const referencję i kopiowało w funkcji. W miejscach krytycznych wydajnościowo można zrobić std::move() i całkiem usunąć kopiowanie.

W jak sensie byłoby co innego? Znaczy rozumiem różnice między const referencją a referencją, ale jeśli chodzi o niejednoznaczność, to wystąpiłaby ona w obu przypadkach, niewazne czy byłaby funkcja z argumentem T& czy const T&
Dokładnie w tym sensie. Przeciążanie funkcji tak że raz argument jest wejściowy, a raz wyjściowy nie jest najlepszym pomysłem. To że się da nie znaczy że jest sens.
P-177445
latajacaryba
Temat założony przez niniejszego użytkownika
» 2020-08-11 00:51:57
Okej, już jasne, dziękuję za pomoc :)
zamykam
P-177446
« 1 »
  Strona 1 z 1