Vyhodnocování výrazů v C++

cppist

Vyhodnocování výrazů v C++
« kdy: 27. 09. 2017, 21:44:45 »
Ahoj, hraju si s novými třídami v C++ a chtěl jsem použít “variant” pro reprezentaci a vyhodnocení jednoduchých aritmetických výrazů. Napsal jsem tedy generický typ Expr dědící z
Kód: [Vybrat]
variant<T,Add<Expr<T>>,Mul<Expr<T>>> ovšem překladač hlásí, že šablonu nelze specializovat (když chci třeba Expr<int>, překladač hlásí “field has incompatible type Expr<int>”). Nevíte, kde je chyba?
A to jsem se ani nedostal k vyhodnocování, kde mi použití “index()” přijde dost jako bad practice, jak byste to vyhodnotili vy?


.

Re:Vyhodnocování výrazů v C++
« Odpověď #1 kdy: 27. 09. 2017, 22:57:00 »
Budu předpokládat, že debil jsem já a poprosím o jednodušší popis, ze kterého to pochopím...

anonym

Re:Vyhodnocování výrazů v C++
« Odpověď #2 kdy: 27. 09. 2017, 23:20:02 »
Ze std::variant se vůbec nemá dědit. Klasický visitor pattern funguje díky polymorfismu, ale hodnoty v std::variant se mají zpracovávat pomocí std::visit, viz http://en.cppreference.com/w/cpp/utility/variant/visit.

Aoidhghean

Re:Vyhodnocování výrazů v C++
« Odpověď #3 kdy: 27. 09. 2017, 23:45:16 »
Ze std::variant se vůbec nemá dědit. Klasický visitor pattern funguje díky polymorfismu, ale hodnoty v std::variant se mají zpracovávat pomocí std::visit, viz http://en.cppreference.com/w/cpp/utility/variant/visit.
Dědičnosti nic nebrání, ačkoliv zde by stačilo using pro zpřehlednění a snadnou rozšiřitelnost. Problém je ovšem v tom, že definice Expr je rekurzivní.

lopata

Re:Vyhodnocování výrazů v C++
« Odpověď #4 kdy: 28. 09. 2017, 03:28:28 »
Dědičnosti nic nebrání
std::variant nemá virtuální destruktor, takže dědit z toho je mírně řečeno problematické.


Aoidhghean

Re:Vyhodnocování výrazů v C++
« Odpověď #5 kdy: 28. 09. 2017, 08:19:57 »
Dědičnosti nic nebrání
std::variant nemá virtuální destruktor, takže dědit z toho je mírně řečeno problematické.
To je celkem logické, většinou nemá moc smysl chtít mít tu třídu virtuální. U boost::variant bývalo zvykem dědit, ale možná jen kvůli absenci generického using jak forma typového aliasu. Nicméně problém toho kódu je, že na součet dvou výrazů potřebuje pointer, nejspíš unique_ptr, on i takový jinak mnohem flexibilnější Swift vyžaduje u rekurzivních typů uvedení indirect (boost míval recursive_wrapper, ale v STL to dost překopali).

lopata

Re:Vyhodnocování výrazů v C++
« Odpověď #6 kdy: 28. 09. 2017, 10:52:41 »
To je celkem logické, většinou nemá moc smysl chtít mít tu třídu virtuální.
Bázovou třídu, ze které se dědí, opravdu má smysl chtít mít virtuální. Jinak se totiž stane, že někdo přetypuje pointer na bázovou třídu, smaže ji a bude se hrozně divit, že se mu nevolá destruktor. V lepším případě si toho všimne, v horším případě tam ten bug zůstane a projeví se až někdy v produkci.

Aoidhghean

Re:Vyhodnocování výrazů v C++
« Odpověď #7 kdy: 28. 09. 2017, 10:57:31 »
To je celkem logické, většinou nemá moc smysl chtít mít tu třídu virtuální.
Bázovou třídu, ze které se dědí, opravdu má smysl chtít mít virtuální. Jinak se totiž stane, že někdo přetypuje pointer na bázovou třídu, smaže ji a bude se hrozně divit, že se mu nevolá destruktor. V lepším případě si toho všimne, v horším případě tam ten bug zůstane a projeví se až někdy v produkci.
Tak jasně, je nutné vědět, že v tomto případě nemůžu mít svůj destruktor. U toho boost::variant podtřídy jen přidávají metody pro pohodlnější práci. Navíc variant má value sémantiku a dělat na něj pointer je prasárna. Jo, není to blbuvzdorné, ale to plyne z faktu, že boost velmi sofistikovanými podivnostmi umožňoval užitečné věci, které jinak nešly. A pak se navrhlo C++11/14/17, abychom se toho zbavili.

Veverak

Re:Vyhodnocování výrazů v C++
« Odpověď #8 kdy: 28. 09. 2017, 12:49:56 »
Ahoj, hraju si s novými třídami v C++ a chtěl jsem použít “variant” pro reprezentaci a vyhodnocení jednoduchých aritmetických výrazů. Napsal jsem tedy generický typ Expr dědící z
Kód: [Vybrat]
variant<T,Add<Expr<T>>,Mul<Expr<T>>> ovšem překladač hlásí, že šablonu nelze specializovat (když chci třeba Expr<int>, překladač hlásí “field has incompatible type Expr<int>”). Nevíte, kde je chyba?
A to jsem se ani nedostal k vyhodnocování, kde mi použití “index()” přijde dost jako bad practice, jak byste to vyhodnotili vy?

Abych ti odpovedel, to co jsi napsal nemuze byt skompilovane z principu. variant neni nic jineho nez fancy objekt nad union{}.
Union funguje tak ze compiler vyhradi v pameti misto na nejvetsi datovy typ, ktery das do unionu a pak na "miste" toho unionu muze byt pritomny jakykoliv z datovych typu toho unionu.

Tj. z principu tehle definice, muzes do unionu /variantu dat jen datove typy u kterych je _jasne_ jakou maji velikost v pameti.

Coz v tenhle moment se nedivim ze nejde, protoze Expr pri rozhodovani o sve velikosti ... musi nastavi velikost na zaklade Add<Expr<T>>, ktery ale zavisi zase na velikosti Expr...  A jakou velikost ma ted compiler zvolit?

Kód: [Vybrat]
typedef std::variant<T, std::unique_ptr<Add<T>>> Effort;

je alternativa ktera funguje