V Φlßnku Visual C++, tentokrßt vizußlnφ v tomto Φφsle Chipu jsem se zmφnil,
₧e implementace jazyka C++ ve Visual C++ .NET verze 2003 (VC03) se ve srovnßnφ
s p°edchozφ verzφ z roku 2002 (VC02) v²znamn∞ p°iblφ₧ila po₧adavk∙m standardu
[1]. V tomto Φlßnku se podφvßme na zm∞ny ve srovnßnφ s p°edchozφ verzφ
a na to, kde nesoulad z∙stal.
Je t°eba upozornit, ₧e jde o zm∞ny, kterΘ se b∞₧n²ch program∙ zpravidla p°φliÜ
nedotknou, nicmΘn∞ v programech, kterΘ vyu₧φvajφ ähraniΦnφô vlastnosti p°ekladaΦe
(nebo standardu jazyka), mohou hrßt roli.
Implementace jazyka C ve Visual C++ .NET 2003 vychßzφ ze starÜφ verze standardu [2] z roku 1990. To znamenß, ₧e v∞tÜina novinek standardu [3] z roku 1999 zde chybφ. PodrobnΘmu popisu novinek standardu [3] jsme se v∞novali v Φlßncφch [4], [5] a [6], a proto se zde omezφme na struΦn² v²Φet. KonkrΘtn∞ jde p°edevÜφm o:
ò deklaraci restringovan²ch ukazatel∙ (klφΦovΘ slovo restrict);
ò umφst∞nφ deklarace kdekoli mezi p°φkazy;
ò deklaraci lokßlnφch automatick²ch polφ s hornφ mezφ zadanou nekonstantnφm v²razem;
ò deklarace polφ s klφΦov²mi slovy restrict nebo static v indexu jako parametr∙ funkcφ (äkvalifikßtory typuô v indexu);
ò nemo₧nost pou₧φvat univerzßlnφ jmΘna znak∙ v identifikßtorech;
ò datovΘ typy pro reprezentaci komplexnφch Φφsel;
ò hlaviΦkov² soubor stdint.h obsahujφcφ typedefy pro alternativnφ oznaΦenφ cel²ch Φφsel a pro rozÜi°ujφcφ celoΦφselnΘ typy;
ò p°φstup k v²poΦetnφmu prost°edφ pro reßlnß Φφsla;
ò pou₧φvßnφ pojmenovan²ch inicializßtor∙ slo₧ek polφ a struktur;
ò vytvß°enφ literßl∙ typ∙ polφ nebo slo₧ek;
ò identifikßtor __func__ v t∞le funkce atd.
K dispozici jsou (64bitovΘ) celoΦφselnΘ
typy long long a unsigned long
long. Jde o alternativnφ oznaΦenφ pro starΘ znßmΘ typy __int64
a unsigned __int64, kterΘ lze samoz°ejm∞ pou₧φvat
i nadßle. (Poznamenejme ale, ₧e typy __int64 a unsigned
__int64 p°edstavovaly rozÜφ°enφ, zatφmco typy long
long a unsigned long long jsou souΦßstφ novΘho
standardu jazyka C.)
Dßle je ve Visual C++ .NET k dispozici klφΦovΘ slovo _inline
(mφsto nov²m standardem jazyka C zavedenΘho inline), kterΘ û podobn∞ jako
v C++ û vyjad°uje po₧adavek, aby funkce byla p°elo₧ena tak, aby b∞₧ela co
nejrychleji. Poznamenejme, ₧e standard ne°φkß, jak toho dosßhnout. M∙₧e to
b²t tak, ₧e se bude na mφsto volßnφ vklßdat t∞lo volanΘ funkce, m∙₧e se ale
pou₧φt i jin² trik û to je ponechßno na implementaci.
K dispozici je takΘ komentß° zaΦφnajφcφ dv∞ma lomφtky (po vzoru C++).
Jazyk C++ je podstatn∞ rozsßhlejÜφ a
odchylky od standardu stejn∞ jako zm∞ny v n∞m jsou v∞tÜinou pom∞rn∞ nenßpadnΘ,
tak₧e se jim budeme v∞novat podrobn∞ji. Poznamenejme, ₧e jako podklad k tomuto
Φlßnku mi poslou₧il internetov² materißl [4] v∞novan²
p°edchozφ verzi, tedy Visual C++ .NET verze 2002.
Alternativnφ reprezentace operßtor∙
Vzhledem k tomu, ₧e nßrodnφ klßvesnice v mnoha jazycφch neobsahujφ znaky &,
^, | a dalÜφ, po₧aduje standard (v oddφlu 2.11), aby implementace nabφzely
alternativnφ reprezentace operßtor∙ &, ^, | , &=,
^=, |= a n∞kter²ch dalÜφch klφΦov²mi slovy and,
xor, or, and_eq atd.
VC03 tato klφΦovß slova neobsahuje. Pokud je chceme pou₧φvat, musφme vyu₧φt
preprocesoru a maker definovan²ch v hlaviΦkovΘm souboru <iso646.h>.
Koenigovo vyhledßvßnφ
Oficißlnφ nßzev tohoto nßstroje znφ vyhledßvßnφ jmen v zßvislosti na parametrech
(argument-dependent name lookup) a je popsßn ve standardu v oddφlu 3.4.2.
Jde o to, ₧e p°i volßnφ funkce nebo operßtoru se jeho deklarace hledß nejen
v aktußlnφm prostoru jmen, ale i v prostoru jmen, v n∞m₧ jsou definovßny typy
parametr∙. Nap°φklad nßsledujφcφ program je podle standardu sprßvn² a
namespace A
{
struct S{};
void f(S){}
}void main()
{
A::S s;
f(s);
}
zavolß se v n∞m funkce A::f(), i kdy₧ nenφ explicitn∞
kvalifikovßna a prostor jmen A jsme ve funkci main()
nijak nezp°φstupnili.
Ve VC02 fungovalo Koenigovo vyhledßvßnφ pouze pro p°etφ₧enΘ operßtory, nikoli
vÜak pro obyΦejnΘ funkce. P°edchozφ program tedy p°ekladaΦ odmφtl; ve VC03
funguje Koenigovo vyhledßvßnφ u₧ podle po₧adavk∙ ve standardu a uveden² program
se p°elo₧φ bez problΘm∙.
Obor prom∞nnΘ deklarovanΘ v podmφnce p°φkazu if
Nejprve si p°ipome≥me, ₧e standard jazyka C++ dovoluje deklarovat v podmφnce
p°φkazu if prom∞nnou a inicializovat ji; hodnotou podmφnky je pak hodnota
p°i°azenß tΘto prom∞nnΘ. Jestli₧e platφ nap°. deklarace
int f() { // N∞jakß funkce vracejφcφ int
return 1;
}lze napsat
if(int n = f())
NecoDelej(n);
else
NedelejNic(n);
Prom∞nnou n lze pou₧φvat v obou v∞tvφch p°φkazu if. Standard (oddφl 6.4) ale nedovoluje v blocφch p°φkazu if deklarovat novou prom∞nnou se stejn²m jmΘnem. NapφÜeme-li
if(int n = f()) {
int n = 21; // Chyba - nelze
NecoDelej(n);
} else {
int n = 78;
NedelejNic(n); // Chyba - nelze
}
m∞l by p°ekladaΦ tyto deklarace oznaΦit za chybnΘ. P°edchozφ verze, VC02,
takovΘto deklarace tolerovala; souΦasnß verze zde sprßvn∞ ohlßsφ chybu.
Poznamenejme, ₧e v blocφch vno°en²ch do t∞chto dvou blok∙ dovoluje standard
identifikßtor n pou₧φt k jin²m ·Φel∙m; omezenφ se t²kß pouze blok∙ na änejvyÜÜφ
·rovniô, tedy na ·rovni p°φkazu_1 a
p°φkazu_2 v syntaktickΘm popisu p°φkazu if.
KlφΦovΘ slovo export u Üablon
Standard v kap. 14 p°edpoklßdß, ₧e v deklaraci Üablony bychom m∞li mφt mo₧nost
pou₧φt klφΦovΘ slovo export, nap°.
export template <typename T> void f(T);
nebo
export template <typename T> class X;
a definici Üablony zapsat nap°. v jinΘm souboru. Od zavedenφ tohoto klφΦovΘho
slova si °ada lidφ slibovala mo₧nost utajit zdrojov² k≤d Üablonov²ch knihoven,
nebo¥ m∞lo umo₧nit zapisovat do hlaviΦkov²ch soubor∙ pouze deklarace v uvedenΘm
tvaru.
VC03 ho nepodporuje, stejn∞ jako p°edchozφ verze VC02. D∙vod je prost² û jde
o jednu z nejproblematiΦt∞jÜφch konstrukcφ ve standardu a p°edpoklßdß se,
₧e novß verze standardu jazyka C++ ji odstranφ. V souΦasnΘ dob∞ ji, pokud
vφm, implementuje jedin² p°ekladaΦ na sv∞t∞ a ukazuje se, ₧e vede k poruÜenφ
n∞kter²ch pravidel zßkladnφ filozofie C++ û nap°. v podstat∞ znemo₧≥uje odd∞len²
p°eklad r∙zn²ch Φßstφ programu.
Operßtor new
Podle standardu (oddφl 3.7.3) operßtor new p°i ne·sp∞ÜnΘ alokaci pam∞ti vyvolß
v²jimku typu std::bad_alloc. V p°edchozφ verzi VC02
vracel v p°φpad∞ ne·sp∞chu hodnotu 0, v souΦasnΘ
verzi se ji₧ chovß podle standardu.
StarΘ chovßnφ (vracenφ nuly) lze nastavit p°epφnaΦem p°ekladaΦe.
Vedle toho lze podle standardu (18.4) nastavit chovßnφ operßtoru new p°i ne·sp∞ÜnΘ
alokaci pomocφ standardnφ funkce set_new_handler().
Tato funkce oΦekßvß jako parametr ukazatel na funkci typu void bez parametr∙.
Volßnφm set_new_handler() definujeme funkci, kterß
se mß volat v p°φpad∞ ne·sp∞ÜnΘ alokace; tato funkce m∙₧e bu∩ ukonΦit program,
nebo se pokusit zφskat volnou pam∞¥ uvoln∞nφm nepot°ebn²ch objekt∙. (Jde tedy
o jakousi variantu garbage collectoru.) Po zavolßnφ
tΘto funkce se operßtor new pokusφ o novou alokaci.
Ve VC03, stejn∞ jako ve VC02, je mφsto funkce set_new_handler()
k dispozici funkce _set_new_handler(), kterß oΦekßvß
ukazatel na funkci s parametrem typu size_t a vracejφcφ
hodnotu typu int. Parametr tohoto handlenu vyjad°uje velikost pot°ebnΘ pam∞ti
a vracenß hodnota vyjad°uje, zda se poda°ilo pam∞¥ zφskat (0 znamenß ne·sp∞ch).
╪et∞zcovΘ konstanty
P°edchozφ verze, VC02, dovolovala m∞nit °et∞zcovΘ konstanty za b∞hu programu.
Ve VC03 jsou tyto konstanty souladu se standardem (odst. 2.13.4) typu const
char[] a jsou ulo₧eny v pam∞ti urΦenΘ pouze pro Φtenφ. Pokus o zm∞nu
°et∞zcovΘ konstanty vyvolß b∞hovou chybu.
P°et∞₧ovßnφ Üablon funkcφ obyΦejn²mi funkcemi
èablony funkcφ lze p°et∞₧ovat obyΦejn²mi funkcemi se stejn²m jmΘnem. Jestli₧e
lze v programu pou₧φt obyΦejnou funkci nebo funkci vytvo°enou podle Üablony,
mß podle standardu (14.5) p°ednost obyΦejnß funkce.
VC03 ji₧ toto pravidlo respektuje, tak₧e nap°. program
#include <iostream>
using namespace std;template<typename T>
void f(T)
{
cout << "Ve funkci void f(T)" << endl;
}void f(int)
{
cout << "Ve funkci void f(int)" << endl;
}int main()
{
f(4);
return 0;
}zavolß nynφ neÜablonovou verzi a vypφÜe
Ve funkci void f(int)
Standard jazyka dovoluje inicializovat tzv. POD-struktury a prom∞nnΘ skalßrnφch
typ∙ zßpisem podobn²m volßnφ konstruktoru.
Nap°φklad takto:
struct bod // POD-struktura
{
int i; double j;
};void main()
{
bod b = bod();
}Podobn∞ m∙₧eme napsat
int m = int();
Tyto (mo₧nß na pohled podivnΘ) konstrukce byly do jazyka C++ zavedeny kv∙li
Üablonßm, aby bylo mo₧nΘ pou₧φvat skalßrnφ typy, POD-struktury a objektovΘ
typy stejn²m zp∙sobem.
VC02 tento zp∙sob inicializace nepodporoval, VC03 ji₧ ano.
Poznamenejme, ₧e pod oznaΦenφm POS-struktury se skr²vajφ struktury ve smyslu
jazyka C, tj. se vÜemi slo₧kami ve°ejn∞ p°φstupn²mi, bez virtußlnφch metod
a bez konstruktor∙. OznaΦenφ POD je zkratkou anglick²ch slov plain old data
(n∞co jako äobyΦejnß starß dataô).
Deklarace ukazatele na funkci
VC02 dovoloval uvΘst v deklaraci ukazatele na funkci specifikaci implicitnφch
hodnot parametr∙. VC03 to v souladu se standardem nedovoluje.
Vno°enΘ Üablony
èablona m∙₧e b²t souΦßstφ t°φdy nebo Üablony t°φdy; pak hovo°φme o vno°enΘ
Üablon∞ (standard 14.5.2). Standard p°itom dovoluje uvΘst v deklaraci Üablony
t°φdy pouze deklaraci, nikoli definici vno°enΘ Üablony metody. Nap°φklad takto:
template <typename T>
class Alfa
{
public:
// Deklarace, nikoli definice
template<typename U> int comp(const U&);
// Definice
template<typename U> Alfa(int x):i(x){}
private:
int x;
};template<typename T>
template<typename U>
int Alfa<T>::comp(const U& u)
{
return true;
}void main()
{ // N∞jakΘ pou₧itφ
}
Zde Üablona t°φdy Alfa obsahuje vno°enΘ Üablony konstruktoru a metody comp().
P°itom Üablona konstruktoru je uvnit° Üablony t°φdy Alfa definovßna, kde₧to
Üablona metody comp() je pouze deklarovßna a je
definovßna dßle.
VC02 dovoloval pouze uvΘst celou definici uvnit°, tj. deklaraci Üablony metody
comp() oznaΦoval za chybnou. VC03 ji₧ tuto situaci
zvlßdne.
Parcißlnφ specializace Üablon
Parcißlnφ specializace Üablon (standard 14.5.4) p°edstavuje zp∙sob, jak definovat
zvlßÜtnφ p°φpady Üablon objektov²ch typ∙ pro urΦitΘ typy nebo skupiny typ∙
v p°φpad∞ typov²ch parametr∙ nebo pro urΦitΘ konkrΘtnφ hodnoty netypov²ch
parametr∙. Podφvejme se na p°φklad:
// Primßrnφ Üablona (nejobecn∞jÜφ definice)
template <typename T, int i>
struct A
{
T f();
};template<typename T, int i> T A<T,i>::f()
{ return i;
}// Parcißlnφ specializace pro jakΘkoli T
// a pro hodnotu 2
template<typename T>
struct A<T, 2>
{
T f(){return 555;}
};
void main()
{
A<double, 2> x; // Zde se pou₧ije specializovanß Üablona
double d = x.f();
}
V deklaraci prom∞nnΘ x ve funkci main()
se pou₧ije druhß verze Üablony, nebo¥ druh² parametr Üablony mß hodnotu 2,
a pro tu je k dispozici parcißlnφ specializace.
VC02 parcißlnφ specializaci Üablon nepodporoval, VC03 ji podporuje.
Parcißlnφ uspo°ßdßnφ Üablon funkcφ
Standard sice nedefinuje parcißlnφ specializaci Üablon funkcφ, umo₧≥uje vÜak
jejich p°et∞₧ovßnφ a tzv. parcißlnφ °azenφ: Lze-li k vytvo°enφ instance Üablony
pou₧φt n∞kolika Üablon, pou₧ije se ta, kterß nejlΘpe odpovφdß skuteΦn²m parametr∙m
(standard 14.5.5.2). Podφvejme se na p°φklad:
// T°i r∙znΘ Üablony funkce
template<class T> void f(T a){}
template<class T> void f(T* a){}
template<class T> void f(const T* a){}void main()
{
const int a = 10;
const int *p = &a;
f(p);
}
Zde se pou₧ije funkce vytvo°enß podle t°etφ Üablony, nebo¥ typ skuteΦnΘho
parametru odpovφdß nejblφ₧e typu parametru prßv∞ tΘto Üablony.
VC02 parcißlnφ uspo°ßdßnφ Üablon funkcφ nepodporuje, VC03 ano.
V²jimky
V deklaraci funkce lze specifikovat v²jimky, kterΘ se mohou z tΘto funkce
rozÜφ°it. K tomu slou₧φ klφΦovΘ slovo throw, uvedenΘ
za prototypem a nßsledovanΘ seznamem typ∙ dovolen²ch v²jimek uzav°en²m v zßvorkßch.
PrßzdnΘ zßvorky znamenajφ, ₧e se z danΘ funkce nesmφ rozÜφ°it ₧ßdnß v²jimka.
Pokud se z funkce rozÜφ°φ v²jimka nedovolenΘho typu, program zavolß funkci
unexpected_exception(), kterß typicky okam₧it∞ ukonΦφ
program.
VC03, podobn∞ jako ve VC02, specifikaci v²jimek v deklaraci funkce nepodporuje.
P°esn∞ji: dovoluje ji pou₧φt, nicmΘn∞ typ v²jimky, kterß se z funkce rozÜφ°φ,
nekontroluje. V²znam mß pouze specifikace throw(),
kterß °φkß, ₧e se z funkce nesmφ rozÜφ°it ₧ßdnß v²jimka, a specifikace throw(...),
kterß °φkß, ₧e se z funkce smφ rozÜφ°it jakßkoli v²jimka. Specifikace throw(typ)
je ekvivalentnφ specifikaci throw(...).
Zde tedy nedoÜlo ve srovnßnφ s p°edchozφ verzφ k ₧ßdnΘ zm∞n∞.
Funkce uncaught_exception()
Funkce uncaught_exception() umo₧≥uje zjistit, zda
je v dob∞ jejφho volßnφ aktivnφ n∞jakß v²jimka. I kdy₧ to vypadß podivn∞,
je to d∙le₧itΘ nap°. v destruktorech objektov²ch typ∙. P°i vzniku v²jimky
se, jak znßmo, volajφ destruktory lokßlnφch automatick²ch instancφ objektov²ch
typ∙ a z nich se v tomto p°φpad∞ nesmφ rozÜφ°it dalÜφ v²jimka. Pro zjiÜt∞nφ,
zda je destruktor volßn p°i v²jimce, nebo za normßlnφ situace, se hodφ prßv∞
tato funkce. Podφvejme se na schematick² p°φklad:
#include <exception>
#include <iostream>
#include <string>class Test
{
public:
Test( std::string msg ) : m_msg( msg )
{
std::cout << "In Test::Test(\"" << m_msg << "\")" << std::endl;
}
~Test( )
{
std::cout << "In Test::~Test(\"" << m_msg << "\")" << std::endl
<< " std::uncaught_exception( ) = "
<< std::uncaught_exception( )
<< std::endl;
}
private:
std::string m_msg;
};// uncaught_exception vrßtφ v destruktoru true
// pro objekt vytvo°en² uvnit° bloku try, nebo¥
// destruktor je volßn jako souΦßst ·klidu zßsobnφkuint main( void )
{
Test t1( "mimo blok try " );
try
{
Test t2( "uvnit° bloku try " );
throw 1;
}
catch (int i) {
}
}Tento program vypφÜe:
In Test::Test("mimo blok try ")
In Test::Test("uvnit° bloku try ")
In Test::~Test("uvnit° bloku try ")
std::uncaught_exception( ) = 1
VC02 a starÜφ verze Visual C++ funkci uncaught_exception() sice implementovaly, ale vracela za vÜech okolnostφ false. Ve VC03 ji₧ funguje.
Prostor jmen stdext
Standardnφ knihovna jazyka C++ podle souΦasnΘ normy neobsahuje heÜovou tabulku
ani ₧ßdn² podobn² nßstroj. D∙vod je prost² û standardizaΦnφ komise je prost∞
nestihla navrhnout a schvßlit. V d∙sledku toho nabφzejφ r∙znΘ implementace
tΘto knihovny vlastnφ verze heÜovacφch kontejner∙. Ve VC02 a VC03 to jsou
Üablony hash_map<>, hash_multimap<>,
hash_set<> a hash_multiset<>.
(PodobnΘ je to ostatn∞ i v jin²ch implementacφch.) Ve VC02 byla tato nestandardnφ
rozÜφ°enφ umφst∞na v prostoru jmen std spolu se standardnφmi souΦßstmi knihovny;
ve VC03 jsou p°emφst∞na do prostoru jmen stdext. Pokud je pou₧ijeme äpostaruô,
s prostorem jmen std, p°ekladaΦ vypφÜe varovßnφ. (Lze si ovÜem p°edepsat,
₧e mß hlßsit chybu.)
Na zßv∞r
Je z°ejmΘ, ₧e implementace C++ ve Visual C++ .NET prod∞lala v²znamnΘ zm∞ny
k lepÜφmu; i kdy₧ se neshoduje pln∞ se standardem, °ada mφst, kde se od standardu
odchylovala p°edchozφ verze, byla opravena. Na druhΘ stran∞ implementace jazyka
C se prakticky nezm∞nila, stßle jde o jazyk C podle normy ze zaΦßtku devadesßt²ch
let s °adou rozÜφ°enφ.
Odkazy
1. International standard ISO/IEC 14882 û 1998. Programming languages û C++.
2. International standard ISO/IEC 9899 û 1990. Programming languages û C.
3. International standard ISO/IEC 9899 û 1999. Programming languages û C.
4. Compiler Limits. http://msdn.microsoft.com/library/
5. M. Virius: CΘΦko na p°elomu tisφciletφ (1). Chip
9/2002, str. 130.
6. M. Virius: CΘΦko na p°elomu tisφciletφ (2). Chip
10/2002, str. 154.
7. M. Virius: CΘΦko na p°elomu tisφciletφ (3). Chip
11/2002, str. 160.