Fórum Root.cz

Hlavní témata => Vývoj => Téma založeno: Ondřej Novák 12. 10. 2018, 09:50:17

Název: C++ konverze na const reference
Přispěvatel: Ondřej Novák 12. 10. 2018, 09:50:17
Zdravím.

Stává se mi, že když napíšu něco na blog o programování, odněkud se vyrojí spoustu znalců normy, kteří ji znají zpamětí i po zpátku a hned vědí, co jsem udělal špatně, co řeší lépe std knihovna, se skrytým odkazem "nauč se pořádně normu a standardní knihovnu, než sem něco napíšeš". Upřímě tyto znalce obdivuju, protože mají čas ty stohy dokumentace studovat. A proto je teď moc prosím o pomoc.

Potřebuji toto:

Mám šablonu klasicku
Kód: [Vybrat]
template<typename T> void foo(...). Předpokládá se, že uživatel šablony T explicitně určí, například

Kód: [Vybrat]
foo<int>(...) nebo
Kód: [Vybrat]
foo<int &&>(...)
A nyní hledám v std něco, co mi z T udělá typ vhodný pro předání argumentu. Dle nějakých směrnic se doporučuje aby


Takže bych to viděl že.
Kód: [Vybrat]

T -> const T &
const T & -> const T &
T & -> T &
T && -> T &&
T * -> T *
scalar<T> -> T

Já si tohle dokážu samozřejmě napsat pomocí částečných specializací. Ale nechce se mi věřit, že ve standardní knihovně nic takového není. Ať hledám jak hledám, nic nemůžu najít. Pomůžete mi? Zase nechci vypadat jako někdo, kdo nemá nastudováno.

PS: Forward to není. Forward je funkce, já potřebuju typ.

Dík.
Název: Re:C++ konverze na const reference
Přispěvatel: Danny 12. 10. 2018, 10:04:39
typedef?
Název: Re:C++ konverze na const reference
Přispěvatel: lopata 12. 10. 2018, 10:20:26
Dá se použít std::is_ z type traits: https://en.cppreference.com/w/cpp/header/type_traits
Název: Re:C++ konverze na const reference
Přispěvatel: JSH 12. 10. 2018, 10:30:44
V první řadě je mi dost podezřelé, proč by ta funkce měla být takhle parametrizovatelná. Co má ta funkce dělat? Nejsem si úplně jistý, co takováhle funkce vůbec může dělat, pokud musí být schopná zpracovat úplně cokoliv. Třeba to jde i jinak a jednodušeji.

Jinak bych si ten problém asi zjednodušil :
- Vykašlal bych se na "Pouze jednoduché typy předávat hodnotou" a všechno předával referencí. Při inlinování to překladač zoptimalizuje a bez inlinování je to tak velká funkce, že se nějaká dereference projeví jen v hodně vzácných případech.
- Zvážil bych, jestli vůbec řešit &&. Co jsem zatím vypozoroval, tak funkce co bere jak lvalue tak rvalue referenci ji akorát někam forwardne. Pro cokoliv jiného už to chce zase dvě přetížené verze, co dělají něco jiného.

Takže bych to viděl na dvě praktické možnosti :
Kód: [Vybrat]
template<typename T> void foo( const T & );
T bude vždycky hodnota. Kdybych tam potřeboval nějak dostat referenci tak použiju std::ref. A T navíc překladač odhadne.
Ve vzácných případech bych použil forwarding referenci (někdy se jí říká univerzální) :
Kód: [Vybrat]
template<typename T> std::enable_if_t<potrebny_fujtajxl> foo( T && );
Když to má sežrat jak lvalue-ref tak rvalue-ref, tak to stejně můžu ledat tak někam forwardnout. A použil bych to jen výjimečně, páč je to obvykle zbytečný overkill.

Ta pravidla spíš doporučení. Pokud můžeš, tak preferuj tohle. Pokud by to mělo přidat kopec práce, tak se to nevyplatí.

Fakt si nedokážu představit, k čemu by se dalo použít to, co chceš. Proto IMO nic takového ani není ve standardní knihovně. Není to natolik potřeba aby se tím někdo zabýval.
Název: Re:C++ konverze na const reference
Přispěvatel: JSH 12. 10. 2018, 10:40:30
Ještě aby to nevypadalo, že jenom trollím :

V C++11 je std::decay a v c++14 std::decay_t. Bez toho scalar a && by to šlo udělat jako
Kód: [Vybrat]
template<typename T> void( const std::decay_t<T> & );
V C++20 přibude i remove_cvref, což by pro tvé účely taky mohlo stačit. Decay si prosím pořádně pročti v manuálu. Odstraní to const a reference, ale navrch to dělá i další věci. Fakt bych ti nedoporučoval to používat, pokud jde udělat jinak.
Název: Re:C++ konverze na const reference
Přispěvatel: Ondřej Novák 12. 10. 2018, 10:46:16
Tak jistě, ta funkce ty argumenty bude někam forwardovat a aniž bych chtěl zabihat do podrobnosti, je určena k volání nějakých uživatelských lambda funkci. Už jen to kdy by to forwardovalo argumenty do std::function, kam málokdy člověk napíše const Gadget & jako argument, přestože tam pak lze bindnout funkci která to const v argumentaci ma.

Dalším zadrhelem je, že to chci použít v param packu, takže overload použít nejde. Forward taky použít nejde protože typy je třeba znát dopředu a příklad s funkcí je jen pro zjednodušení, nejspíš to bude třída, která bude mít takovou funkci.

Uživatel může použít T, T&, const T &, T && a mělo by se to předat správně s tím, že T chci během zpracování tahat jako referenci. Nevím jestli funguje const T && &, ale mám pocit že ne, proto ta specializace
Název: Re:C++ konverze na const reference
Přispěvatel: lopata 12. 10. 2018, 10:52:58
V C++11 je std::decay a v c++14 std::decay_t. Bez toho scalar a && by to šlo udělat jako
Čistější je něco jako std::conditional<std::is_(arithmetic, rvalue_reference...)<T>, foo, bar>. Ale je otázka, jestli to opravdu není předčasná optimalizace.
Název: Re:C++ konverze na const reference
Přispěvatel: vrazda 12. 10. 2018, 10:58:10
Pokud chces explicitne specifikovat na interface ruzne chovani pro ruzne typy, muzes pouzit sfinae. std::enable_if a std::enable_if_t

http://coliru.stacked-crooked.com/a/9caf611c4365ede8
Název: Re:C++ konverze na const reference
Přispěvatel: JSH 12. 10. 2018, 11:03:13
Tak jistě, ta funkce ty argumenty bude někam forwardovat a aniž bych chtěl zabihat do podrobnosti, je určena k volání nějakých uživatelských lambda funkci. Už jen to kdy by to forwardovalo argumenty do std::function, kam málokdy člověk napíše const Gadget & jako argument, přestože tam pak lze bindnout funkci která to const v argumentaci ma.

Dalším zadrhelem je, že to chci použít v param packu, takže overload použít nejde. Forward taky použít nejde protože typy je třeba znát dopředu a příklad s funkcí je jen pro zjednodušení, nejspíš to bude třída, která bude mít takovou funkci.

Uživatel může použít T, T&, const T &, T && a mělo by se to předat správně s tím, že T chci během zpracování tahat jako referenci. Nevím jestli funguje const T && &, ale mám pocit že ne, proto ta specializace

Tak pokud chceš forwardovat, tak použij forwarding reference a std::forward. Občas je potřeba tu funkci omezit přes enable_if.
Kód: [Vybrat]
template<typename ... Args>
void foo( Args && ... args ) { bar( std::forward<Args>(args)...); }

std::function právě bere přesně typy parametrů a žádné konverze tam nedělá. Pokud už píšeš typy explicitně, tak už je můžeš zrovna napsat tak jak potřebuješ.

To, aby musel uživatel ručně dodávat templatové parametry funkce mi nepřijde jako dobrý nápad. Pokud musí ručně dodávat parametry aby rozlišil lvalue a rvalue reference, tak jsem si zatraceně jistý, že je to blbý nápad.

Pokud nechceš zabíhat do podrobností, tak můžu jenom hádat, že ses pravděpodobně upnul k nějakému nešikovnému způsobu řešení. To, co chceš dělat, mi přijde silně podezřelé.
Název: Re:C++ konverze na const reference
Přispěvatel: Ondřej Novák 12. 10. 2018, 11:05:04
Decay je přesný opak

Dám jiný priklad

Kód: [Vybrat]
template<typename T>
void call(T &&fn)

...
call([=]{...})

Ačkoliv vse je podle učebnic a nejefektivnější perfekt forwarding, přesto dost často vidím funkci call specializovanou tak že se T předává hodnotou, což poznám na dvojnasobnem volání konstruktorů capturovanych hodnot.

Ale možná to je Bug v Gcc. Nemám po ruce reprezentativní priklad momentálně
Název: Re:C++ konverze na const reference
Přispěvatel: Ondřej Novák 12. 10. 2018, 11:16:00
Šablona s param pakem není řešení. Uvědomuji si že jsem zapomněl zmínit že ta funkce bude nejspíš virtuální (typy argumentů obdrží trida, ve které bude definována)

Ale máš pravdu, mohu se na to vykašlat a nechat to na userovi, když to nedá jako referenci ať se mu to tam kopíruje (ale přijde mi to nefér vůči němu)
Název: Re:C++ konverze na const reference
Přispěvatel: JSH 12. 10. 2018, 12:01:04
Citace
Decay je přesný opak.
Ten decay je tam proto, abych pak mohl vždycky ten const a referenci přidat. Abych nikdy nedostal dvojitou referenci.
Šablona s param pakem není řešení. Uvědomuji si že jsem zapomněl zmínit že ta funkce bude nejspíš virtuální (typy argumentů obdrží trida, ve které bude definována)
No ale tohle kompletně mění celý dotaz. :D
A vypadá to spíš jako nějaká interní třída, se kterou by uživatel moc neměl přijít do styku. Koukni se, jak se běžně dělá type-erasure (třeba pro implementaci toho std::function). Možná chceš udělat tohle.
Citace
Ale máš pravdu, mohu se na to vykašlat a nechat to na userovi, když to nedá jako referenci ať se mu to tam kopíruje (ale přijde mi to nefér vůči němu)
Nefér může taky být to, že ať uživatel dělá co chce, bude tam mít referenci. Když už musí absolvovat celý ten opruz s psaním parametrů, tak už tam ten jeden ampersand napsat zvládne. Pokud mi knihovna nedovolí tam napsat přesně to, co chci, tak ať mě to nenutí psát vůbec.
Název: Re:C++ konverze na const reference
Přispěvatel: Ondřej Novák 12. 10. 2018, 14:12:33
No já jsem si uvědomil, že je docela problém provést konverzi parameter packu. On ten parameter pack je docela oříšek. Trochu sem si s tím začal hrát tady

http://cpp.sh/26g5x

Příklad skončí kompilační chybou, to je záměr, protože součástí chyby je výsledek. Výsledkem je vygenerování funkce, kde jsou všechny typy vyžadovány s const T &, ačkoliv původní parameter pack to neměl. Je to primitivnější verze. Problém je totiž vlastní konverze a pak použití konvertovaného param paku na vygenerování prototypu funkce. V příkladu se to řeší tak, že se vygeneruje třída, která obdrží konvertovaný parameter pack a v ní je definovaná ta funkce, která se instanciuje v daném prototypu.

Ale že bych tohle někde viděl ve standardní knihovně? :) Vůbec hledal jsem co všechno se dá dělat s param.pakem a ... nic moc, všichni z toho hned dělají tuple. Jenže nenašel jsem, jak z tuple vyrobit prototyp funkce.
Název: Re:C++ konverze na const reference
Přispěvatel: JSH 12. 10. 2018, 14:41:27
No já jsem si uvědomil, že je docela problém provést konverzi parameter packu. On ten parameter pack je docela oříšek.
Zkonvertovat jednotlivé parametry packu není až takový problém :
Kód: [Vybrat]
template<typename ... Args>
class X {
  public:
    auto foo(something_t<Args> ... x)
    {
        return bar( x... );
    }
};
Za something_t si dosaď svou transformaci pro jeden typ. Psát takhle obecný kód ale není vůbec sranda. O tomhle i členové c++ komise beze srandy říkají, že to nedávají. A běžně v tom dělají chyby.

Citace
http://cpp.sh/26g5x

Prosím, zkoukni https://www.youtube.com/watch?v=xnqTKD8uD64 Přesně na tenhle typ kódu naráží Sutter v úvodu. Ta tvoje konverze packu je write-only. Je to totálně nečitelné, neudržovatelné a muset to po tobě upravovat, tak si asi uhryžu nohu jak liška v pasti.

Prosím, moc tě prosím. Přestaň hledat ve standardní knihovně rovnák na svůj ohýbák. Evidentně děláš něco moc moc špatně, takže tě to vede k těmhle prasárnám.
Název: Re:C++ konverze na const reference
Přispěvatel: Ondřej Novák 12. 10. 2018, 17:47:35
Nepřijde mi, že dělám něco špatně. Zaprvé řeším nějaký problém. A samozřejmě, že bych to mohl napsat zdlouhavě a řešit to třeba v runtime, ale proč? Třeba jsem řešil to, jak používat lambda funkce a benefitovat z toho, že lambda funkce mají vnitřní stav. Ona s tím totiž standardní knihovna moc nepočítá, třeba při práci se std::function mi každá kopie vytváří i kopii vnitřního stavu. Ale s vnitřním stavem se počítá, protože máme přece klíčové slovo mutable.

 Nebo třeba že lambda funkce je vlastně objekt, ale nemá this.

Protože hodně používám různé callbacky a asynchroní volání, které je realizované lambdo, někdy kvůli vnitřnímu stavu musím místo lambdy napsat třídu, a to je zdlouhavé, kód se stěhuje na jiné místo a nepřehledné, není jasné, kudy kód pokračuje v asynchroním zpracování. Je to víc nepřehledné, než pochopit vnitřní stav. Snažil jsem se nějak řešit tento problém. A to proto, že jakkoliv se zeptam na stack overflow, končím pouze u začátečnických dotazů.


Za druhé, ano, neznám všechny zákoutí práce s param packy, protože třeba vím, že konverze lze napsat do kódu pokud funkci volám, ale nevěděl jsem, že to funguje i pokud definuju parametry funkce. Holt se musím učit.

Takže díky za tu diskuzi.
Název: Re:C++ konverze na const reference
Přispěvatel: Xxxxxx 12. 10. 2018, 23:41:16
A kdyz si tohle vsecko prectu a kolik nervu a energie to stalo,
tak si rikam zlate ciste C.  Do void* strcim cokoliv, pointery byl skvely vynalez, pak C++ vymyslelo reference pozdeji & & a kam jsme dosli.
Podle me do riadnej riti.
doufam, ze se to casem v C++50 usadi, ale zatim je to posahana magie a hnus.

Javascript ma silene frameworky, java je obludarium samo o sobe, C++1x i bez knihoven je blazinec.

Nebyt nucen pouzivat C++ a javu tak chci Go!
Název: Re:C++ konverze na const reference
Přispěvatel: Bacsa 12. 10. 2018, 23:51:58
A kdyz si tohle vsecko prectu a kolik nervu a energie to stalo,
tak si rikam zlate ciste C.  Do void* strcim cokoliv, pointery byl skvely vynalez, pak C++ vymyslelo reference pozdeji & & a kam jsme dosli.
Podle me do riadnej riti.
doufam, ze se to casem v C++50 usadi, ale zatim je to posahana magie a hnus.

Javascript ma silene frameworky, java je obludarium samo o sobe, C++1x i bez knihoven je blazinec.

Nebyt nucen pouzivat C++ a javu tak chci Go!
Rust a Swift jsou taky dobře navržené.
Název: Re:C++ konverze na const reference
Přispěvatel: Xxxxxx 13. 10. 2018, 00:08:53
A kdyz si tohle vsecko prectu a kolik nervu a energie to stalo,
tak si rikam zlate ciste C.  Do void* strcim cokoliv, pointery byl skvely vynalez, pak C++ vymyslelo reference pozdeji & & a kam jsme dosli.
Podle me do riadnej riti.
doufam, ze se to casem v C++50 usadi, ale zatim je to posahana magie a hnus.

Javascript ma silene frameworky, java je obludarium samo o sobe, C++1x i bez knihoven je blazinec.

Nebyt nucen pouzivat C++ a javu tak chci Go!
Rust a Swift jsou taky dobře navržené.

Ted si ctu rust a libi se mi ze = pro zakladni typy kopiruje a pro slozite typy dela move, kopirovani je pomoci clone. To je spravne.
Název: Re:C++ konverze na const reference
Přispěvatel: Xxxxxx 13. 10. 2018, 00:19:17
Ten rust vypada super,  reference jsou const, muze byt jedina reference kdyz neni const, parametr funkce se movne kdyz neni reference, parada.