Podrobný článek vítám.
Zajímalo by mě, jak je to s čárkou za poslední větví konstrukce match. Většinou ji tam máte a jednou ne.
Vzhledem k tomu ze to vypadá jako kopie jazyka Erlang, dovolim jsi odpovědět.
Jedna se o defaultni větev. Když pravidla před tim nezaberou, tak udělej tohle.
Pokud takova větev v rozhodování není a nic nezabere, v Erlangu se vyvolá výjimka. Tady nevím.
Ty funkce co se s nimi muže pracovat jako s daty jako by z oka vypadly funs z Erlangu.
Defaultní větev se dělá pomocí _. Čárka za posledním prvkem nemá žádný význam (podobně jako v jiných jazycích), je to příjemné pro přidávání dalších prvků (nebo odebírání), jelikož není nutné podle kontextu čárku přidávat nebo mazat.
Však se v článku píše: "Ve skutečnosti překladač kontroluje, zda je _ použit, protože je vyžadováno, aby větve v konstrukci match pokryly všechny možné hodnoty testovaného výrazu"
a hned pod tím je ukázka. Takže to vypadá, že Rust si to kontroluje, Erlang ne (vůbec ne?) a C na to kašle a jen někdy vypíše warning, když se dobře nastaví přepínače. Těžko říct, co je nejlepší.
Tak podle me je nejlepsi aby se to kontrolovalo, tak jak to dela Rust ci jazyk D. Teda jazyk D ma i verzi switche ktery default nepotrebuje pokud se switch provadi nad vyctovym seznamem a jsou pouzity vsechny prvky s vyctoveho seznamu - coz dava smysl.
Rust kontroluje jestli jsou pokryty všechny možnosti a že všechny větve vrací stejný datový typ. Jde to docela ruku v ruce. Je to někdy docela otrava když vím, že nějaká možnost nemůže nastat (třeba x%2 je jen 0 nebo 1, ale match vyžaduje i vše ostatní). Tam se dá použít macro třeba unreachable!() nebo panic!() které obě mají univerzální návratovou hodnotu a schodí program pokud na ně dojde.
Co se '_' týče, je třeba to brát jako pattern. '_' nevytváří žádnou dočasnou proměnnou. Stejně tak by se pro vyčerpání všech možností dala proměnná pojmenovat (nebo všechny hodnoty pokrýt ručně). Patterny se pak vyskytují i u deklarací proměnných. Docela dobré když funkce vrací enum s několika hodnotami a já chci jen jednu. Nebo pro for cylkus když nijak nepotřebuju i.
Čárky nejsou povinné pokud je ve větvi tělo {} a na poslední větvi, ale automatické formátovače (třeba rustfmt plugin pro cargo) je za těli odebírá a na poslední příkazy přidává.
Je vhodny Rust pro numericke vypocty? Obsahuje nejake optimalizovane knihovny napriklad pro maticove nasobeni?
Asi hledá srozumitelný jazyk, kde rychlost nezabije tím, že v tom jazyce napíše for cyklus (což se může stát v R, Pythonu i Julii).
Třeba implementace Kalmanova filtru? Jako jsou řešení jako např. Cython, ale pokud by to nějaký jazyk zvládal nativně, bylo by to fajn :-)
Pokud vím, tak konkrétně Julia si nevede vůbec špatně a snaží se ty low level konstrukce optimalizovat, viz třeba http://julialang.org/benchmarks/
Tudíž by mě zajímalo, jestli to někomu přijde jako taková tragédie, aby lovil zrovna ve vodách Rustu, který je podle mě z hlediska zápisu pro tyto účely míň vhodný, než třeba Python+Cython, Julia nebo já nevím - třeba Nim.
> Pokud vím, tak konkrétně Julia si nevede vůbec špatně
Otázkou je, jak jsou optimalizace v Julii robustní - aby člověk nestrávil hodiny hledáním důvodů, proč se určitý kousek kódu nezoptimalizoval, jak měl - viz třeba http://zverovich.net/2016/05/13/giving-up-on-julia.html nebo https://www.reddit.com/r/Julia/comments/3x6tg9/why_is_this_loop_in_julia_slower_than_the_python/
Jasně, je to interpretovaný jazyk s JIT, jeho JIT je možná v něčem horší než JIT pro Python z odkazu, má pomalejší rozjezd než třeba Python, Julia je ve verzi 0.5 a na nedokonalost má nárok. Otázka je, co z toho komu vadí. Docela bych souhlasil s pohledem, který zde psali jiní - na prototypování (třeba) Python, na velkou aplikaci, která pravidelně chroustá spoustu dat, (třeba) Rust.
Nevím jak vám se to ukazuje, ale já vidím Rust na Githubu mezi 5 nejaktivnějšími a nejpopulárnějšími jazyky.
Nějaká nuimerické a algebraické knihovny tu jsou, ale je to dost fragmentované:
num - další numerické typy (bigint, complex, ...)
nalgebra - dobře optimalizovaná lineární algebra pro nízké dimenze
ndarray - N-dimenzionální matice a metody na nich
Letos na RustConf byli nějací chlapíci s machine learningem a docela shrnuli co od toho očekávat:
https://www.youtube.com/watch?v=lY10kTcM8ek
Není to moc pro prototypování protože to není dynamickty typovaný jazyk, není interpretovaný a nemá moc vizualizační nástroje. Ale je rychlý, dobře se paralelizuje, dobrý networking, error handling atd.
Ano, to je velmi dobré shrnutí - na prototypy to není dobrý jazyk, to raději použít IPython notebook nebo i to Rko. Ale pokud někoho pálí výkon už má navržené algoritmy, tak směle řekněme "in Rust we trust" :-)
Ty jsi mi ukradl me moto. Dokonce jsem si vyrobil tricko.
Kasli na rust a pouzivej jazyk D :D.
http://mir.rocks
http://blog.mir.dlang.io/glas/benchmark/openblas/2016/09/23/glas-gemm-benchmark.html
Proc nepouzit C++, např. knihovny Armadillo či Eigen. Stále žiji s představou, když chci max speed, tak C či C++.
Tak kdyz ma C, C++ a Rust stejnej backend (LLVM), tak ten speedup je teoreticky ne? Nebo jinak - jaka vlastnost by delala z C/C++ takova dela v porovnani s jinym kompilovanym jazykem bez GC?
Muzou tam byt navic treba ruzne run-time testy, napr. meze poli, ukazatele, ...
Kdyz jsem kdysi cetl o vypujcenych ukazatelich v Rustu tak me treba pripadalo ze nektere veci budou muset testovat v run-time. Ale treba jsou chytri a vyresili to jinak... :-)
Potom podle semantiky jazyka nemusi nektere veci jit implementovat tak efektivne, treba dynamic dispatch. Nerikam ze tohle je pripad Rustu.
Dalsi co me napada je treba pattern matching. Kdybys to v C++ delal "rucne" tak tam treba vyuzijes nejakou znalost problemu a udelas to lip...
Nebo naopak růstu (třeba časem) vymyslí nějakou mikrooptimalizaci a udělá to lépe než vývojář v C++. Podobně jako dnes GCC někdy poráží vývojáře v assembleru (pokud nejsou extrémně zkušení), protože kompilátory jsou v některých mikrooptimalizacích dobré (resp. vývojáři kompilátorů jsou dobří) a optimalizovat to ručně by si vyžádalo tunu zkušeností a času.
"Muzou tam byt navic treba ruzne run-time testy, napr. meze poli, ukazatele, ..." " ... tak me treba pripadalo ze nektere veci budou muset testovat v run-time... " " ... Potom podle semantiky jazyka nemusi nektere veci jit implementovat tak efektivne,... " " ... Dalsi co me napada je treba pattern matching. ..."
Typická ukázka předčasné optimalizace založené na dojmech. Víc k těm nesmyslům asi nemá cenu dodávat.
a jeje, nekomu jsem narusil belief system, aby me ted nezacal kamenovat ;-)
Jinak to co jsem psal neni mysleno jako kritika Rustu, spis me slo o to ukazat ze kdyz 2 jazyky maji stejny backend tak to neznamena ze budou stejne efektivni... ;-)
Kdyz vezmu napr tu kontrolu mezi poli - vubec nevim jestli to rust dela nebo ne - ale jazyk ktery to dela nebude tak efektivni jako jazyk ktery to nedela, i kdyz bude mit stejny backend.
"Kdyz vezmu napr tu kontrolu mezi poli - vubec nevim jestli to rust dela nebo ne..."
Tak si to zjistětě.
You have completely missed the point. Jde o princip, že stejný backend nemusí zajistit stejný výkon, ne o konkrétní případ polí.
Stejně tak Java, Groovy a Scala nad stejným JVM mohou mít jiný výkon, a to někdy dost zásadně. Ano, některé výkonové charakteristiky a optimalizace budou mít podobné, ale dá se – i mezi Javou a Scalou – najít spousta rozdilů, někdy i velkých.
Tak jsem udelal maly testik, aby to nebylo mlaceni prazdne slamy, jednoducha smycka, sum square pro i od 0 do milionu, rust vs c, rustc vs clang, bez optimalizace c o rad rychlejsi, s optimalizaci O3 taky c rychlejsi, ale nechce se mi hrabat v rust deklaracich, abych zjistil presnejsi rozdily pro O3. Jen tak pro info, size rust exe codu 240kB, size c exe codu 8kB.
Mas prosim ty zdrojaky nekde na GitHubu (napriklad). Me by to docela zajimalo, jestli je to semanticky skutecne to stejny. Jinak velikost exe codu, to je pravda, na nejakem mikroradici si zatim dnesni verzi Rustu predstavit nedokazu.
Kdyz mi das mail, tak ti to poslu, rust dnes vidim asi tak po treti, takze jestli je to zcela identicke, to fakt nevim, vysledky jsou kazdopadne stejne, mozna deklarace promennych neni identicka. Kazdopadne zaklad pro rust jsem vzal v tomto clanku, napsat c code je easy. Kompilovano pod Fedorou 24, rustc i clang jsou defaultni verze z Fedory. V nejhorsim to sem muzu pastnout, zdrojaky jsou kratke.
// clang squares.c -o squares_c
// clang squares.c -o squares_c_O3 -O3
#include <stdio.h>
#define MAX 1000000
long square(long x)
{
return (x * x);
}
int main()
{
long long sum = 0;
long i;
for (i = 0; i <= MAX; i++)
{
sum += square(i);
}
printf("%lld\n", sum);
return 0;
}
// rustc squares.rs -o squares
// rustc squares.rs -o squares_O3 -C opt-level=3
fn main()
{
let sequence = 0..1000000;
let squares = sequence.map( | x | {let result = x*x;
result
});
let mut sum:u64 = 0;
for n in squares
{
sum = sum + n
}
println!("{}", sum);
}
V C jenom sčítáš čísla, v rustu je uložíš do seznamu a pak sečteš.
Mas pravdu. Rust se ucit zatim nebudu, kdyz to nekdo prepise do c ekvivalentu, tak to mohu zkompilovat a porovnat to, ja se v tom vice hrabat nebudu.
To není pravda. Range ti nevrací pole, ale iterátor od-do. let squares = sequence.map(funkce) bere iterátor a "napojí" na něj funkci, výsledkem je zase iterátor. A ve for cyklu už se na iterátor volá next dokud nevyprší.
fn main()
{
let mut sum:u64 = 0;
for x in 0..1000000 {
sum += x * x
}
println!("{}", sum);
}
zkus optimalizace, Rust schvalne by default neoptimalizuje vubec, viz FAQ:
Why is my program slow?
The Rust compiler doesn’t compile with optimizations unless asked to, as optimizations slow down compilation and are usually undesirable during development.:
Ale jinak z takto kratkeho programu se moc nezjisti, protoze 6 nebo 10 ms je tak nizka hodnota, ze se do toho nutne zapocita start runtime (to je problem vetsiny mikrobenchmarku). Zacal bych s necim slozitejsim, co psali lidi, kteri ten ktery jazyk znaji:
jo taky prosim zkus (pokud Te to samozrejme jeste zajima) prekladac 1.12, ve Fedore je 1.11
Dik za odkaz. Z tech linku je jasne, ze C a C++ je z hlediska rychlosti stale the best a az tomu tak nebude, tak budu prekvapen.
Moje testy:
Kod D:
import std.range: iota;
import std.stdio : writeln;
import std.algorithm : fold;
enum MAX = 1_000_000UL;
void main()
{
iota(0, MAX).fold!"a + b^^2".writeln;
}
D (ldc bez optimalizace)
13ms
D (ldc optimalizace)
3ms
C (clang bez optimalizace)
13ms
C (clang optimalizace)
2ms
rust (rustc bez optimalizace)
48ms
rust (rustc optimalizace)
5ms
rychlejší by to asi být nemělo, ale malej gold se syntaxí iterátorů:
let sum = (0usize..1_000_000).fold(0, |acc, n| acc + n * n);
nebo pro názornost:
let sum = (0usize..1_000_000).map(|n| n * n).fold(0, |acc, sq| acc + sq);
Rust má podstatně větší standardní knihovnu než C a další věci co v C nejsou (např. panic! macro) a v základu linkuje staticky. Pokud opravdu potřebuješ minimální velikost, musíš si s tím vyhrát trochu víc (hledej bare metal rust). Jinak těžko říct cos tam proved, protože imho jenom smyčka by vyšla zhruba stejně rychlá i v Javě.
Co se velikosti binárka týče, https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html je pěkný čtení.
TL;DR: Rozebírá se tam velikost "Hello World" kde Rust má 650kB a C jen 8,5kB. Je tam něco o debugovacích symbolech (moc nevim co to je) - obsahuje jemalloc pro dynamické alokace ikdyž nejsou v programu, backtrace a jeho stringy ikdyž v programu není panic. Po odstranění dohodle je to na 113kB. Taky knihovny jsou linknutý staticky a ne dynamicky jako u C. Rust má nějaký závislosti na standardní C knihovně, linkuje je staticky, jakýmsi příkazem kompilátoru se dá linknout dynamicky a pak je Rust stejně velký jako C.
Debug symboly je možné odstranit příkazem "strip", stejně jako u jiných binárek, třeba u céčka přeloženého s přepínačem -g). Tím se velikost sníží na cca 360kB (v tom článku to zmiňují). Nevýhodou je, že při ladění přes gdb (lze volat i rust-gdb) nebudou jednoduše k dispozici názvy funkcí atd. gdb pouze lakonicky vypíše:
Reading symbols from hello_world...(no debugging symbols found)...done.
A i tak základní příkazy jako "break main" už nebudou funkční:
(gdb) break main
Function "main" not defined.
Jde to sice obejít, pro distribuci aplikace je možné použít externí symboly (distra to tak běžně dělají, protože většina lidí asi debug symboly nevyužije, prakticky jen pro ABRT, takže proč zaplňovat disk objemnými soubory), ale pro vývoj to asi nemá cenu řešit.
Tezko rict kam az ten optimalizator "vidi". "Moderni" C++ ma tu vyhodu, ze vsechno jsou jen sablony, ktere obsahuji pouze inline metody. Spojenim takovych sablon pak vznikne velice dlouhy kod, na kterem ze muze opimalizator vyradit. Dneska se ale daji podobnym zpusobem optimalizovat i non-inline funkce. Za urcitych okolnosti optimalizotor vi, ze po zavolani nejake jednoduche funkce zustanou registry nezmenene.
No, když Rust, C++, D a další budou kompilovat přes LLVM, pak se lišit moc nebudou a důležité pro volbu jazyka bude to, jak se v něm dělá. To je jednak syntax (každému vyhovuje jiná, někdo se nevzdá té céčkovské, někomu se líbí Rust) a pak (asi hlavně) práce s knihovnama.
A Rust je v tomhle dobrý (poučený z Javy, Pythonu, Nodejs, ....) a přes cargo nabízí snadnou práci s knihovnami (nevím proč tomu říkají crates).
V C/C++ už jsem nějaký rok nedělal, ale s knohovnama byl někdy problém. Hlavně s různými verzemi knihoven.
Taky mě trochu hlava nebere. Rust se srovnává s C, přitom třeba má vlastnosti hodně podobné C++, včetně všelijakých kontrol. Pramení to snad s dodnes opakované a nepravdivé Linusovi mantry o tom, že C++ se pro vývoj kernelu nehodí? Nikdy by nikdo nebyl schopen odpovědět na otázku proč.
BTW: C++ používám pro psaní software pro Arduino. Bez problémů (jen mi chybí výjimky, ale chápu, proč jsou problém)
No to nevim. Zacinal jsem na wiringu ale pak jsem zjistil, ze je to postaveny nad c++ takze jsem to zacal psat ve visual studiu naplno v c++ a wiring pouzil jako knihovnu k desce. Pouzivam sablony, alokace objektu (destruktory to vola), i virtualni funkce (akorat tam v knihovne chybi nejake funkce ktere se predpokladaji ze jsou v libc, da se obejit) ... a prekladac odmitne prelozit kod obsahujici throw.
Pokud to člověk chce apriori považovat za neplatné, tak stejně žádnému důvodu neuvěří. Podle mě je C++ dost strašný jazyk, který vynucuje nešikovný objektový model a přitom nenabízí nic směrem k odolnosti vůči chybám, spíš naopak. Oproti tomu je C jednoduchý a dobře standardizovaný jazyk, který lépe odpovídá tomu, co se děje ve výsledné binárce, lépe se v něm řeší binární i zdrojová kompatibilita, nevynucuje jeden nepříliš dobrý objektový model.
Ale jak říkám, člověk, který se jednou zahrabal v C++ a považuje to za nejlepší věc na světě tohle asi nepobere.
OLOL OLOL
Pane, napsal jsem ve standardním C hru. Pak jsem zkusil C++ a pak jsem se učil Javu a OOP a pak jsem se vrátil zpět k C++ a vzpoměl jsem si, jaká trápení jsem během psaní té hry zažil,jak jsem si vyvíjel systémy, které řešili
- stringy
- virtuální funkce
- lambda funkce (ono známé, předání pointeru na funkci a kontext)
- zapouzdření dat (AI nestvůr v té hře by se realizovala mnohem lépe pomocí objektů)
čisté C? Nikdi více!
A co jako? Mám přesně opačnou zkušenost. Jestli má někdo potřebu se po naučení Javy vracet k C++, tak to ten jeho kód musí být teda pěkná "lahůdka". Raději bych to snad ani nechtěl vidět, natož tam muset něco modifikovat. A soudě dle vašich různých názorů zde v diskusích mám z vás pocit, že přestože "jste napsal hru", tak bych si k vám pro rady ohledně technik programování a softwarového inženýrství rozhodně nechodil.
zajímavý blogpost, i když asi ne se vším se dá úplně souhlasit. Nicméně jako námět na zamyšlení moc pěkné!
IMO ty nejstrašnější vlastnosti (třeba bordel v intech) C++ zdědilo právě od C.
A to je podle me i odpoved na to, proc se rika, ze Rust ma ambici nahradit C. C++ je C s pridanou hodnotou (a hromadou balastu) a +- se v nem porad da psat normalni C. Rust se snazi predefinovat "nastupce C" s minimem balastu a znalostmi z okolniho sveta, ktere autori maji.
Myslim, ze Linus to vysvetloval tim, ze C++ skryva pred programatorem alokaci pameti, coz muze byt v kernelu kontraproduktivni. Napr. diky implicitnim datovym konverzim zavolate metodu a pred jejim faktickym zavolanim si vytvorite hromadu docasnych objektu na stacku.
C a C++ maji "problem" v tom ze nerozlisuji mezi poiterem na stack a pointerem na heap. Snadno muzete na heapu vytvorit instanci, ktera ukazuje na stack vaseho vlakna a vsechno bude krasne fungovat dokud vase vlakno neumre. Takove chyby, jsou jen velice tezko odhalitelne. (I kdyz v C++ se daji udelat smart pointery, ktere neco takoveho nedovoli)
Pokud je to pravda, tak Linus nezná C++, jinak by to netvrdil ... ale dost možná to spíš neřekl nebo to řekl jinak.
C++ nedělá žádné skryté alokace. Možná je dělá knihovna STL. Ta je sice součástí standardu, ale není povinnost ji používat. Objekty lze v C++ nadefinovat tak, že se chovají přesně tak, jak tvůrce vyžaduje, včetně toho, že má pod kontrolou přesný postup co se volá při vytvoření a při destrukci (což taky znamená, že se nemusí dít vůbec nic).
Takže ano, pokud napíšete std::string x = "ahoj", pak se tam alokuje paměť pro řetězec "ahoj". Já z toho důvodu používám objekt, který nazývám StringView, který nic nealokuje. Mimochodem, něco podobného (jako std::string_view) se chystá do C++17. Ale to nebrání tvůrce kernelů si něco podobného vyvinout už od C++98.
Ano, co pro C++ chybí je knihovna pro programování kernelů..., kde by byly do objektů zabalené věci na míru implementované potřebám kernelu.
C++ nedělá žádné skryté alokace.
V originale Linus psal:
any compiler or language that likes to hide things like memory allocations behind your back just isn't a good choice for a kernel.
Nemluvi tedy o skrytych alokacich, ale o jazycich, ktere rady skryvaji veci jako je alokace, coz ma trochu jiny vyznam. Staci se zamyslet nad tim, proc vznikaji vyssi jazyky. Je to prave proto, aby skryvaly jednotlive nudne casti programu a nezatezovaly tim programatora. Kdyz tedy C++ nic neskryva, k cemu je dobre?
Takže ano, pokud napíšete std::string x = "ahoj", pak se tam alokuje paměť pro řetězec "ahoj".
Nejak na tom radku postradam explicitni alokaci pameti. Nebude to tim, ze ji C++ skryva?
C++ samo nic neskryva, skryva ji knihovna
Zrovna tak muzes v C napsat knihovnu kde budou skryte alokace...
C++ ti umoznuje jit na vyssi uroven abstrakce a skryt detaily kdyz chces, ale taky muzes zustat stejne low level jako v C.
char *c = strdup("ahoj")
?kde vidíš explicitní alokací?
?Jak z toho vyčtu, že mám po použití udělat free(c)?
Vidím tam viditelné volání konkrétní funkce (strdup) s konkrétními parametry. Takže, když už volám nějakou (cizí) funkci a nevím co dělá, tak šup do dokumentace. Třeba:
http://man7.org/linux/man-pages/man3/strdup.3.html
... a vida. Je tam jak informace i že volá malloc (a ne nic jiného, třeba new) a je tam i info o free.
Nikde není zamlčeno, že tam (existuje) "mezilehlý" návratový alokovaný prostor, není zamlčeno kde že se vytvoří (heap x stack x ...) a jaké funkce k tomu použil (malloc x new x static ...). A je jasně dáno (tedy, je to dáno na mne, programátora) kdy to místo bude uvolněno, respektive sám určím kdy uvolním.
V C++ na radku:
std::string x = "ahoj"
je taky jasne volani konkretni funkce (konstruktoru). Jenom clovek musi umet C++ aby to tam videl ;-)
Vím moc dobře, jak to tam je a vím (pracuji na low-level, embedded) a že tam s tím dokáži udělat pěkné prasárny. Stačí to toho zapojit pár online a optimalizaci a mírně se to zamotá děděním. Plus problém se systémy, kde např. konstanty (vč. statických stringů) mohou být chápány různě v závislosti na nastavení překladače (a pak jeho následné optimalizaci).
Navíc, sakra záleží na obou stranách přiřazení. Nejjednodušší pak bývá jít se podívat do assembleru, co že mi tam reálně udělal. Někdy to bývá docela překvápko a jsou to 3-4 instrukce a hotovo. Někdy (stačí drobná změna) je z toho alokace na stacku, volání konstruktoru a destruktoru hned za sebou (který nic nutného neudělá, žádné volatile) a pak přiřazení něčeho úplně mimo.
No, rozepisoval bych se moc, zkrátím to. Vracet objekt je věc, která může nadělat dost škody i ve chvíli, kdy člověk myslí, že zná dokonale, jak mu překladač na daný systém funguje.
... a to je to, čím může být c++ nebezpečné. Jak některé věci může skrývat před očima programátora, tedy dělat implicitně i alokace na stacku (minimálně), vlastně jen dle uvážení (doslova, někdy ano někdy ne, dle optimalizace). Je to fajn, dokud funguje a je dost místa a runtime času a nesnaží se optimalizovat - pak obvykle nezkrachuje. Obvykle. Ale jakmile se zajde do hloubky ...
Mel jsem na mysli neco takoveho:
#include <stdio.h>
class Foo
{
public:
Foo(int x) {};
};
class Bar
{
public:
Bar(Foo const &_f)
: f(_f)
{};
Foo const& f;
void print()
{
const void *p = &f;
printf(" pointer: %p \n", p);
}
};
int main()
{
Bar *pf1 = new Bar(Foo(42));
Bar *pf2 = new Bar(42);
pf1->print();
pf2->print();
return 0;
}
Zavolam "new Bar(42);" a vubec si nevsimnu, se vlastne vykona "new Bar(Foo(42));" a mam na heapu instanci, ktera ukazuje na muj zasobnik.
PS: uz jsem v C++ dlouho nedelal, takze jestli je to blbost, tak me opravte.
Popravdě jsem si myslel, že s deklarací konstruktoru `Bar(Foo const &_f)` se kvůli ampersandu výraz `new Bar(Foo(42))` (popř. `new Bar(42)`) ani nezkompiluje, protože to není left-side-expression. Ale ono se to fakt zkompiluje: http://codepad.org/5Bc1s0Az
Zřejmě se to skutečně alokuje na stacku.
Na svou obranu dodám, že C++ mě neživí a beru to spíše jako součást všeobecného rozhledu.
Nikdo netvrdi ze C++ je bezpecny jazyk ale lze v nem psat bezpecne. Vse je o lidech a o stabni kulture. Tak napriklad vyse uvedeny priklad se prelozi, protoze const T & se da aplikovat i na docasne objekty a nepredpoklada se, ze se budou drzet jako const reference, jejich platnost by mela byt zajistena do navratu z funkce (constructoru). Pokud uz to ale nekdo takhle napise (i ja to tak nekdy delam) tak se tam dava klicove slovo explicit, cimz se zabrani automaticke konverzi (ale nezabrani to kdyz to nekdo udela manualne, holt je to v lidech)
Skompiluje jedna se o rvalue referenci. Jedna z velmi popularnich a kontroverznich vlastnosti v C++. Napriklad jazyk D to neumi a je za to casto lidmi ze sveta C++ kritizovan. Ja osobne nejsem velky fanda teto funkcionality
Právě, je to rvalue, ale intuitivně bych čekal, že kompilátor přijme jen lvalue. Muset to vyčlenit do proměnné by způsobilo jen to, že by bylo jasnější, co se děje.
Proto by konstruktor s jedním argumentem (když to není copy/move) měl být deklarovaný jako explicit, pokud opravdu nechci definovat typovou konverzi. Ale úplně stejně špatně je i new Bar(Foo(42)) v inicializaci pf1. Když si třída Bar drží referenci na argument konstruktoru, tak by tam asi měla být jako pojistka ještě jedna deklarace konstruktoru: Bar(Foo &&_f) = delete
pouziti explicit, nebo vymazani rvalue copy constructoru resi ten problem jenom castecne.
"Vychytraly" programator muze snadno udelat treba:
Foo f;
Bar *b = new Bar(f);
Hlavni problem je ze kdyz si nekde ukladam ukazatel na objekt, tak ten objekt by nemel mit kratsi zivotnost nez ten ukazatel.
V dokumentaci ke tride bar by melo byt jasne napsane - uklada si ukazatel na parametr, je potreba zajistit zivotnost... a programator by se tim mel ridit :-)
V tom bych neviděl až takový problém. Tady je už poněkud zřetelnější, jakože blbost tu programátor vyvádí. Jasně, není to 100% memory-safe, ale o to se C++ ani nikdy nesnažilo.
Např. v libc++ std::string x = "ahoj" nic navíc nealokuje, protože krátké řetězce (do cca 20 znaků) se vejdou přímo do objektu std::string. Navíc pro třídy z STL je možné definovat vlastní alokátory a tím libovolně změnit způsob, jak se dynamická paměť alokuje a dealokuje.
I v kódu kernelů psaných v C se spousta věcí skrývá ve funkcích, v horším případě v makrech, v nejhorším případě v kódu generovaném pomocí ad-hoc skriptů při buildu. Ekvivalentní kód v C++ používající šablony, lambdy a inline funkce je obvykle o dost srozumitelnější. Nebo naopak programátor musí vědět, že má zavolat přesně definovanou posloupnost operací. A když to udělá, tak musí přidat do každé následující větve kódu jiné operace. Typicky takto vypadá použití zámků. Třídy a RAII v C++ v takových situacích poskytují lepší řešení.
Ja si treba myslim, ze C je na kernel lepsi z hlediska code-review a kontroly spravnosti kodu. C je 'jednodussi' jazyk - clovek si vystaci s par klicovymi slovy nekolika malo pravidly a jedna z nejslozitejsich veci je priorita operatoru. Naproti tomu v C++ clovek musi umet ruzne konstruktory, musi vedet ze jeden zapis (treba Foo a = b) muze volat ruzne konstruktory, v zavisloti na typu vyrazu b a tak podobne. Za mne je C++ sice casto lepsi nez C, ale je to bastl, takze na to aby clovek mohl _cist_ program se toho musi naucit mnohem vic nez v pripade C. Jine jazyky vyssi urovne se tomu vetsinou vyhly, protoze mely vyrazne konzistentnejsi vyvoj (ale existuji vyjimky jako treba PHP, to je imho jeste horsi bastl).
Ekvivalentní kód v C++ používající šablony, lambdy a inline funkce je obvykle o dost srozumitelnější.
To je hezke pri normalnim vyvoji, v pripade jadra to muze byt zasadni nevyhoda, protoze na jednu stranu ziskate sice srozumitelnejsi kod, ale zase ztracite predstavu o tom, jak dany kod bude vypadat po prelozeni do assembleru/strojoveho kodu.