Közvetlen bérlet (C++)

A közvetlen átvitel ( Eng.  Perfect Forwarding ) egy idiomatikus mechanizmus paraméterattribútumok átvitelére a C++ nyelv általánosított kódjának eljárásaiban . A C++11 kiadásban szabványosították STL funkcionalitással és továbbítási hivatkozások szintaxisával , és egységesítették a variadic sablonokkal való használatra [1] [2] .  

Közvetlen átadást használunk, ha az általános kód függvényei és eljárásai szükségesek ahhoz, hogy paraméterezett argumentumaik alapvető tulajdonságait változatlanul hagyják, azaz [1] :

A közvetlen átadás gyakorlati megvalósítása a nyelvi szabványban std::forwarda fejlécfájlból <utility>[3] [4] egy függvény segítségével valósul meg . Ennek eredményeként a -hivatkozások speciális következtetési szabályainak &&és hajtogatásának kombinációja lehetővé teszi egy olyan funkcionális sablon létrehozását, amely tetszőleges argumentumokat fogad el, azok típusának és alapvető tulajdonságainak rögzítésével ( rvalue vagy lvalue ). Ennek az információnak a mentése előre meghatározza az argumentumok átadásának lehetőségét más függvények és metódusok hívásakor [5] .

Háttér

Paraméterek speciális viselkedése – Ideiglenes linkek

Tekintsük az elemi objektumot két konstruktorral – az egyik egy mezőt másol az std::stringből, a második mozog.

class Obj { nyilvános : Obj ( const std :: string & x ) : mező ( x ) {} Obj ( std :: string && x ) : mező ( std :: mozgatás ( x )) {} // std::mozgatás szükséges!! privát : std :: stringfield ; _ }

Az első konstruktor túlterhelés a leggyakoribb a C++03-ban. És a második std-ben:: mozogj, és ezért.

A kívül található string&& paraméter ideiglenes (rvalue) hivatkozás, és egy elnevezett (lvalue) objektum átadása nem lehetséges. A függvényen belül pedig ennek a paraméternek a neve (lvalue), azaz string&. Ez a biztonság kedvéért történik: ha egy függvény, amely egy string&& karakterláncot vesz fel, összetett adatkezelésen megy keresztül, lehetetlen véletlenül megsemmisíteni a string&& paramétert.

A kérdések akkor kezdődnek, amikor sok a paraméter - 4, 8, 16 ... konstruktort kell készítenie.

osztály Obj2 { nyilvános : Obj ( const std :: string & x1 , const std :: string & x2 ) : field1 ( x1 ), field2 ( x2 ) {} Obj ( const std :: string & x1 , std :: string && x2 ) : field1 ( x1 ), field2 ( std :: move ( x2 )) {} // ...és további két privát túlterhelés : std :: string mező1 , mező2 ; }

Kétféleképpen nem lehet entitásokat szorozni, az "érték+mozgás" idióma és a metaprogramozás , utóbbihoz pedig egy második C++11 mechanizmus is készült.

Linkek ragasztása

A hivatkozás összeomlását ez a kód magyarázza a legjobban . 

a One = int && ; használva Kettő = Egy & ; // majd Kettő = int&

Az átadott referenciákhoz való átadáskor nem csak a függvénynek átadott paraméter típusát derítik ki, hanem azt is értékelik, hogy rvalue vagy lvalue . Ha a függvénynek átadott paraméter egy lvvalue , akkor a behelyettesített érték egyben az lvalue hivatkozása is lesz . Ennek ellenére meg kell jegyezni, hogy egy sablonparaméter-típus &&-referenciaként való deklarálása érdekes mellékhatásokkal járhat. Például szükségessé válik egy adott típusú összes helyi változóhoz explicit inicializátor megadása, mivel ha ezeket lvvalue paraméterekkel használjuk, a típuskövetkeztetés a sablon példányosítása után egy lvvalue hivatkozás értékét rendeli hozzájuk, ami a nyelvi követelménytől függően rendelkeznie kell inicializálóval [6] .

A linkragasztás a következő mintákat teszi lehetővé:

class Obj { nyilvános : sablon < classT > _ Obj ( T && x ) : mező ( std :: előre < T > ( x )) {} // ugorj előre és csináld jól privát : // alább elmagyarázzuk, miért nem tudod megtenni explicit forward függvény nélkül std :: string mező ; }

Az ilyen ideiglenes hivatkozásokhoz a fordítók speciális szabályokat adtak hozzá [7] , amelyek miatt…

  • ha T=karakterlánc, akkor leszObj(string&&)
  • ha T=string&, akkor leszObj(string&)
  • ha T=const string&, akkor ez leszObj(const string&)

Következmény: nem lehet automatikusan tudni, hogy egy hivatkozás ideiglenes-e

Térjünk vissza az Obj::Obj sablonkonstruktorhoz. Ha nem veszi figyelembe az idegen típusokat, hanem csak a karakterláncot, akkor három lehetőség közül választhat.

  • T=karakterlánc, példányosítva: , belül x=string&.Obj(string&&)
  • T=karakterlánc&, példányosítva: , belül x=string&.Obj(string&)
  • T=const string&, példányosítása itt : x=const string&.Obj(const string&)

A harmadik lehetőség jó, de az egyszerű típuskövetkeztetés nem tudja megkülönböztetni az elsőt a másodiktól. De az első változatban a std::move szükséges a maximális teljesítményhez, a másodikban veszélyes: a mozgással történő hozzárendelés „kibelezi” a karakterláncot, ami még hasznos lehet.

Megoldás: std::forward

Térjünk vissza a sablonkonstruktorunkhoz.

sablon < classT > _ Obj ( T && x ) : mező ( std :: előre < T > ( x )) {}

A sablon csak sablonokban használatos (nem sablon kódban van elég ). Megköveteli, hogy a típust kifejezetten meg kell adni (egyébként nem különböztethető meg a -tól ), és vagy nem csinál semmit, vagy -re bővül . std::forwardstd::moveObj(string&&)Obj(string&)std::move

Idióma "érték szerint + áthelyezés"

A második módja annak, hogy ne szorozzuk meg az entitásokat: a paraméter érték alapján kerül átadásra . std::move

class Obj { nyilvános : Obj ( std :: karakterlánc x ) : mező ( std :: mozgatás ( x )) {} privát : std :: stringfield ; _ }

Amikor egy objektum mozgatása lényegesen "könnyebb", mint a másolás, általában nem sablon kódban.

Jegyzetek

  1. 1 2 Vandewoerd, 2018 , 6.1 Élő, p. 125.
  2. Horton, 2014 , Perfect Forwarding, p. 373.
  3. std::forward Archiválva : 2019. január 19., a Wayback Machine C++ hivatkozásnál
  4. Vandewoerd 2018 , 15.6.3 Élő, p. 333.
  5. Vandewoerd 2018 , 15.6.3 Élő, p. 332.
  6. Vandewoerd, 2018 , 15.6.2. Átvihető linkek, p. 331.
  7. Vandewoerd, 2018 , 6.1 Élő, p. 127.

Források

  • D. Vandevoerd, N. Josattis, D. Gregor. C++ sablonok. Fejlesztői hivatkozás = C++ sablonok. A teljes útmutató. - 2. - Szentpétervár.  : "Alfa-könyv", 2018. - 848 p. - ISBN 978-5-9500296-8-4 .
  • I. Horton. Ivor Horton Beginning Visual C++ ® 2013. - John Wiley & Sons, Inc., 2014. - ISBN 978-1-118-84577-6 .

Linkek