#13 KIIV
Nechce se mi s vámi hádat, ale vaše příspěvky na toto téma mi připadají příliš zavádějící a nepřesné.
Pokud vám rozdíl mezi deklarací a definicí funkce připadá jako slovíčkaření, tak jste se, obávám se, minul oborem. Ta delete deklarace je skutečně pouze deklarací, kterou říkáte, že žádná definice nikdy nebude. Chápu, jak to myslíte, ale pokud chcete něco takového vysvětlovat nováčkům, tak není možné používat termíny vágně. A navíc tento rozdíl je klíčový pro vysvětlení způsobu, jak se kopírování zakazovalo před C++11.
Připouštíte, že kopírování se dá vyhnout, ale přidáváte "musíte sakra dobře vědět, co děláte." Mohl byste být konkrétní, co tím máte na mysli? Protože realita je taková, že vyhnout se kopírování je právě ta jednoduchá věc i pro začátečníky: prostě ho jednoduše bezmyšlenkovitě zakazujete všude. Dobře vědět, co děláte, musíte naopak v době, kdy kopírování něčeho chcete povolit.
Dále tvrdíte, že pro vyhnutí se kopírování potřebujete C++11. Mohl byste být konkrétní a vysvětlit, co konkrétně tomu v C++98 brání? Protože já jsem posledních 12 profesních let strávil na překladači, který se C++11 ani nepřiblížil a nechtěnému kopírování jsem se naprosto úspěšně bránil. Postup jsem popsal ve svém příspěvku výše.
Jak konkrétně souvisí STL s kopírováním? STL kontejnery se umí kopírovat/movevovat. OK, ale to je prostě jejich normální vlastnost, kterou můžete i nemusíte využít. Objekty vkládané do kontejnerů musí být kopírovatelné/movevatelné. Pokud kopírování/move zakážete, objekt prostě vložit nejde. S čím je tedy konkrétní problém?
U polymorfismu je skutečně nutné používat referenci nebo ukazatel, naprosto souhlasím. Zakázáním kopírování třídy toto vynutíte - objekt pak nejde použít jinak. Vůbec to není o tom, zda to někdo "zvládne". Zapomenout napsat & u argumentu funkce je zcela běžná programátorská chyba, kterou udělá i zkušený profesionál. Pokud kopírování nezakazujete s tím, že to přece "zvládáte", ani svatý překladač vám nepomůže.
Kopírovací a move C++ sémantika se až na dvě výjimky (níže) u polymorfního objektu nehodí nikdy. Ne proto, že jsem to řekl, ale proto, že kopírování/move je vlastnost vyhodnocovaná při překladu, což je v přímém rozporu s očekávaným chováním polymorfního objektu. A psát objekty, které se chovají jinak, než jejich uživatelé očekávají, je poměrně zaručená cesta do pekla.
Pokud chcete kopírovat polymorfní objekt, dělá se to postupem, který se označuje jako klonování. Tzn. objekt má na rozhraní virtuální metodu (typicky pojmenovaná clone), která vytvoří kopii. Je praktickým zvykem tuto metodu interně implementovat pomocí protected copy konstruktoru (první ze zmiňovaných výjimek).
Druhá výjimka jsou výjimky. Zde se překladač při odvíjení zásobníku postará o kopii/move správného typu, takže nedochází ke slicingu. Ovšem ke slicingu může dojít a také dochází, pokud výjimku nechytnete referencí - což je přesně důvod, proč se výjimky chytají referencí.
Poznámka o move objektů, které se někde registrují nebo si např. drží uvnitř sebe pointer někam do sebe, je samozřejmě zcela správně. Zbývá dodat, že zákazem kopírování, tak jak jsem ho popsal, zároveň zakazujete i move sémantiku, protože překladač vám nevygeneruje move konstruktor/operátor, pokud máte deklarovaný příslušný copy konstruktor/operátor.