Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in /home/www/novacisko.cz/subdomains/bredy/init.php on line 11

Warning: mysql_connect(): Headers and client library minor version mismatch. Headers:50562 Library:100020 in /home/www/novacisko.cz/subdomains/bredy/init.php on line 11

Warning: Cannot modify header information - headers already sent by (output started at /home/www/novacisko.cz/subdomains/bredy/init.php:11) in /home/www/novacisko.cz/subdomains/bredy/index.php on line 38
Jak dostat řetězec do parametru šablony - Bredyho blog - Ondřej Novák
Bredyho blog - Ondřej Novák

Jak dostat řetězec do parametru šablony

Současná norma C++ umožňuje jako parametr šablony použít tzv. integrální typy, tedy typy odvozené od int, dále bool, ukazatel a referenci. Ukažme si, existuje způsob, jak tam dostat i řetězec


Problém

template <const char *text>
class Sablona {...}

Kupodivu tato šablona se přeloží, ale to co od toho očekáváme se nestane:

Sablona<"ahoj"> var; //chyba!!

Překladač, který dodržuje poslední normu C++ oznámí chybu, protože konstanta "ahoj" není integrální typ. Jak je tedy možné, že překladač připustil šablonu s typem const char * když stejně nemůžeme takovou konstantu dosadit?

Problém je ten, že parametr typu const char * specifikuje ukazatel na konstantní proměnou typu char. Tedy nikoliv na řetězec, byť jsme na to zvyklí. Aby bylo možné šablonu použít, musíme dosadit ukazatel.

const char znak = 'A';
Sablona<&znak> var;

Zkuste si, že tohle projde.

Jak na řetězec

Dostat tímto způsobem do šablony znak je docela 'kanón na vrabce'. Char je přece integrální typ. To co jsme ale tady skutečně použili je ukazatel, který je taky integrální typ. Dalším integrálním typem je i reference. Zkusme upravit deklaraci šablony

template <const char *&text>
class Sablona {...}

Tímto způsobem jsme deklarovali šablonu přijímající referenci na proměnnou typu const char *. Reference je integrální typ, takže by to mělo projít. Kdo očekává, že se podaří instanciovat šablonu takto...

Sablona<"ahoj"> var; //chyba!!

... opět bude zklamán. Ale máme ještě jednu možnost. Využijme stejného zápisu jako v předchozím případě:

const char *text = "Ahoj";
Sablona<text> var;

A vida, překladač se zdá být spokojen. Uvedený příklad jsem vyzkoušel ve Microsoft Visual Studio 2008. Předpokládám ale, že by to mělo fungovat i v G++. Pokud by se ukázalo, že překladač má problém s referencí, lze to obejít přes ukazatel (budou tam dvě hvězdičky) a s použitím dereference (&).

Popsané řešení není bez 'vad na kráse'. Kosmetickou vadou budiž nutnost deklarovat konstantní proměnnou. O mnoho větším problémem může být fakt, že proměnná nesmí být 'internal linkage'. Jinými slovy níže modifikovaná varianta neprojde:

static const char *text = "Ahoj";
Sablona<text> var; //chyba C2970

Tato drobnost nám defacto zadělává na problém v podobě nebezpečí duplicitních symbolů. Pokud stejnou proměnnou použijeme ve vícero *.cpp souborech, linker bude zmaten, protože pro něj to budou různé symboly se stejným jménem (neuvedením klíčového slova static způsobí, promoměnná se bude exportovat z přeloženého object souboru).

Naštěstí i zde existuje způsob, jak to obejít. To řešení nabízí klíčové slovo namespace bez uvedení jména. Definice říká, že takový namespace vkládá proměnné do namespace s náhodným jménem, tak, aby nebezpečí duplikátních symbolů nehrozilo. Zároveň tyto proměnné exportuje do nadřazeného namespace, takže jsou běžně přístupné. Vylepšené řešení tedy vypadá takto:

namespace { const char *text = "Ahoj"; }
Sablona<text> var; //ok

Tohle opravdu projde a můžete si zkusit, že proměnné nebudou kolidovat, za předpokladu, že jsou uvedeny v odlišných *.cpp. Řešení má ještě jeden drobný zádrhel: Pokud uvedeme deklaraci v hlavičkovém souboru, pak každý *.cpp do kterého se tento soubor vloží založí novou proměnnou. To zároveň povede k vytvoření nové instance šablony. Na velikost kódu a jeho efektivitu to má opravdu nepatrný vliv (optimalizátor stejné řetězce sloučí do jednoho, pouze založí N ukazatelů, každý bude mít jinou adresu). Problém je, že tyto instance budou různé, nebudou tedy kompatibilní. Pokud toto potřebujeme, musíme si pomoci sami. Například kompatibilitu řešit na úrovni base třidy, nebo pomocí konverzních operátorů a konstruktorů (např. zjištěním, že se dvě instance liší o ukazatel, tak porovnat obsah textu a případně převest pomocí reinterpret_cast)

Další využití

Podobný způsob lze použít například pro float, či double, které také nejsou integrální typy:

template <double&init>
class Sablona {...}

namespace { double initVal = 3.14159265; }
Sablona<initVal> var; //ok

Nevýhodou tohoto řešení je, že nelze promenné použít k šablonovým výpočtům, ale aspoň něco. Opět pozor při práci s hlavičkovými soubory.

Závěr

Tohle byl krátký článek připomínající další možnosti šablon. Pokud jste něco takového hledali, tak samozřejmě nezapomeňte hlasovat v anketě pod článkem. Kladné ohlasy vždycky působí jako motivace k psaní dalších podobných článků.

vytvořeno: 4.2.2009 00:23:41, změněno: 4.2.2009 00:23:41
Jsou informace v článku pro Vás užitečné?
  • (7)
  • (0)
  • (2)
  • (0)
  • (3)
Nick nebo OpenID
Vzkaz
 
8.4.2009 21:28:05

Ondra openid

Jo to taky jde, ale vyžaduje ten const char pak někde instanciovat, v nějakém CPP. Je to samozřejmě na volbě použití.

Já se postupně dostávám k šablonám výjímek, které už nebude nutné deklarovat jako třídu, ale jako typedef, který bude obsahovat i popisek chyby :-)
8.4.2009 18:09:30

Sten

Zkus místo anonymního namespace použít extern const char *. Viola, z jednoho hlavičkového souboru máme stejný typ pro všechny *.cpp (alespoň v GCC) :)
9.2.2009 13:00:04

Ondra openid

Díky... no snažím se, víceméně sepisuji poznatky z praxe, jak se říká, předávat myšlenky dalším generacím. A taky proto, že mám poslední dobou pocit, že programovat v C++ přestává být \"in\", což je docela škoda.
4.2.2009 15:39:19

Rene.Stein.myopenid.com openid

Vyborny blog. Diky za zajimave clanky o C++. Pravidelne ctu a vracim se. :)

Podobné články

C++ - Akce a Zpráva jako objekt

Akcí nazývám obyčejné volání funkce, zprávou pak volání metody objektu (jak je chápáno podle OOP). Lze vůbec volání čehokoliv reprezentovat jako objekt? A k čemu je to vlastně dobré?

Jak předělat dvou-parametrovou šablonu na jedno-parametrovou

Popis jednoduchého triku

Dědičnost šablon, CRTP a Invoker

OOP prvky lze používat i při generickém programování pomocí šablon. Dědit šablony je snadné, podívejme se i na polymorfické šablony.

Šablony: Nastavování vlastností tříd

Svět šablon v C++ je velmi zajímavý, pro znalce dokonalý, pro začátečníky šílený a nepřehledný. Následující článek pojednává o dalším kouzlu se šablonami

Seznamy typů a jejich použití

Opět inspirován známým C++ guru: Andrei Alexandrescu jsem se vrhnul na implementaci seznamů typů "po svém". V této části si povíme co to je a k čemu to slouží.
Reklama: