HTML

C++ programozás

Főként C++ programozásról, de lehet szó még C#, D vagy más nyelvről is.

Friss topikok

  • tormanator: A CG-shaderben megírt raytracing 106x gyorsabb, mint egy SSE-utaításokkal futó raytracing. Mindeg... (2011.09.08. 07:00) Csak párhuzamosan!
  • koszperv: @Vorbis: Szia! Köszöntlek, mint a blogom első hozzászólóját. Az enum egyébként tényleg egész haszn... (2009.12.30. 11:44) const vs define

Linkblog

2010.01.21. 00:09 koszperv

Előre!

Eddig volt olyan feature, ami lerövidítette a kódot, volt, ami felgyorsította a kódot, volt, ami kijavított egy régi hibát, és most lesz egy, aminek, nos... használhatósága erősen korlátozott. Ez pedig a perfect forwarding. Ami nem más, minthogy úgy adjuk át a bekapott paramétereket egy belső függvényhívásnak, hogy azok lvalue-rvalue-sága ne sérüljön. Vágjunk hát bele a közepébe. A perfect forwarding így néz ki:

template<typename Left, typename Right>
    auto Add(Left&& left, Right&& right)
        -> decltype(forward<Left>(left) + forward<Right>(right))
{
    return forward<Left>(left) + forward<Right>(right);
}
A perfect forwardingnak gyakorlatilag csak egy általánoscélú library template-ei között van bármi értelme, ezért is egy template-en mutatom be (bár egy ilyen összeadó template-nek nincs semmi értelme, de a célnak megfelel - ráadásul máshol is általában ezzel mutatják be).

Amint látható, az újfajta autós függvényfejlécet alkalmazza. Ez azért szükséges, hogy a decltype-ban már használhassuk az input paramétereket lokális változóként. Egyébként nullpointerekkel kellene bűvészkedni, azt pedig senki nem akar.

A decltype pedig nem csinál mást, minthogy megmondja milyen típusú is a beleírt kifejezés eredménye. Így az Add template-függvény pont olyan típussal tér majd vissza, mint amivel az összeadás. Ez azért fontos, mert egy template-ben egy overloadolt operátor, vagy egy ismeretlen függvény visszatérési típusát nem ismerhetjük. Így viszont könnyedén le tudjuk kérdezni.

A következő trükk az input paramétereknél van. Ha a bejövő típusban van referencia, akkor az rvalue referencia beleolvad, ha nincs, akkor hozzáadódik. Ezáltal a paraméter típusa lvalue referencia lesz, ha az eredeti típus is lvalue referencia volt, és rvalue referencia lesz, ha eredetileg nem volt referencia, vagy rvalue referencia volt. Röviden lvalue referencia lesz, ha lvalue volt, és rvalue referencia lesz, ha rvalue volt.

Van itt még egy trükk: ez pedig a foward. (A forwardnak mindig van template-argumentuma, aminek mindig az eredeti típusnak kell lennie! Ez az argumentum valamilyen bonyolult template bűvészkedés miatt kell oda, de nem tudom racionálisan megindokolni, hogy miért.) A feladata, hogyha lvalue referenciát kap, akkor azt lvalue-ként adja ki, ha pedig értéket vagy rvalue referenciát kap, akkor azt rvalue-ként adja tovább. Ezáltal ha lvalue jön be a template-függvényünkbe, akkor a forward lvalue-t ad tovább, ha pedig rvalue jön be, akkor rvalue-t ad tovább. Hogy ez miért fontos? Két eset lehetséges: a belső függvény lvalue-t vár, és szeretné megváltoztatni, ekkor rvalue-t nem is kaphat; illetve, ha a belső függvény rvalue-t vagy konstans lvalue-t vár, és nem akarja megváltoztatni. Ha egyik feature-t sem szeretnénk elveszíteni, akkor eddig ezt a problémát overloaddal oldhattuk meg eddig. Ez paraméterenként 2 overload. Mármint nem összeadva, hanem összeszorozva, így 3 paraméter már 8 függvényt jelent. Ezeket az overloadokat most nem kell megcsinálni, mert elég egyetlen függvény is hozzá.

Igen jól látjátok, ez a perfect forwarding azért még nem teljesen perfect. Ugyanis csak egy adott paraméterszámú függvényt képes befogadni. Mégha a belső függvény nem is változó paraméterszámú, akkor is külön overloadot kell írni az egyparaméteres, a kétparaméteres, stb függvényekre. Ezt a "fontos" és "roppant sűrgős" problémát remélhetőleg a változó paraméterszámú template-ek megoldják majd.

A decltype-ról érdemes még néhány szót szólni, mert nem azt csinálja, mint amire az ember számítana. A szabvány szerint ez a feladata:

decltype(e)

  1. Ha 'e' egy nem zárójelezett változó, tagváltozó, függvény, metódus, statikus változó, stb, akkor a változó vagy függvény típusát jelenti.
  2. Egyébként, ha 'e' egy függvény- vagy operátorhívás, akkor annak a visszatérési típusát jelenti.
  3. Egyébként, ha 'e' lvalue, akkor a visszaadott típus egy lvalue referencia 'e' típusára.
  4. Egyékbént, 'e' típusa.

int fn() { return 5; }
int i;
struct A { double x; };
A a;

decltype(fn()) x1;            // int
decltype(i) x2;             // int
decltype(a.x) x3;             // type is double
decltype((a.x)) x4(a.x);    // type is const double&
decltype(fn) *x5 = &fn;
        // decltype: int(void) x5: int(void)*
A példák a szabvány alapján vannak. A legdurvább az, hogy ha a.x-et dupla zárójelbe rakjuk akkor referencia lesz belőle. Őszintén szólva nekem ez így roppant bizar lett. Érdekességként még ott az utolsó sor, amiben nem egy függvényhívást teszek a decltype-ba, hanem egy függvényt. Ebben az esetben a függvény típusát adja vissza, nem pedig a függvény visszatérési típusát. Ezt a típust azonban csak függvénypointerként tudjuk értelmesen felhasználni, ezért raktam oda azt a csillagot.

 

Szólj hozzá!

A bejegyzés trackback címe:

https://progcpp.blog.hu/api/trackback/id/tr851689159

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.
süti beállítások módosítása