Sedm kohout∙ na smetiÜti, jeÜt∞ jednou |
V tomto Φlßnku se vracφme k porovnßvßnφ n∞kter²ch b∞₧n∞
dostupn²ch p°ekladaΦ∙ jazyka C++. Na rozdφl od Φlßnku Sedm kohout∙ na smetiÜti z tohoto Φφsla tiÜt∞nΘho Chipu zde najdete nejen vφce technick²ch podrobnostφ, ale takΘ ukßzky
zdrojovΘho k≤du.
Uve∩me si nejprve porovnßvanΘ p°ekladaΦe znovu spolu se
spolu se zkratkami, pomocφ nich₧ se na n∞ budeme v dalÜφm textu odvolßvat.
Jde o
P°ekladaΦe jsou zde se°azeny v abecednφm po°adφ a toto
po°adφ nevyjad°uje ₧ßdnΘ hodnocenφ. Zßkladnφ fakta o nich jsme ji₧
uvedli v Φlßnku Sedm kohout∙ na smetiÜti.
Zde najdete nejen podrobn∞jÜφ porovnßnφ zahrnujφcφ v∞tÜφ poΦet kritΘriφ,
ale i ukßzky zdrojovΘho k≤du.
Pro porovnßvßnφ jsme se rozhodli pou₧φt nßsledujφcφ
kritΘria:
V Φlßnku Sedm kohout∙
na smetiÜti v tiÜt∞nΘm Chipu jsme zkoumali, zda p°ekladaΦe implementujφ
nßsledujφcφ konstrukce po₧adovanΘ standardem [1]:
K nim p°idßme jeÜt∞ nßsledujφcφ:
Tabulka 1 obsahuje v²sledky pro vÜechny porovnßvanΘ p°ekladaΦe.
BC6 | BCX | BCXP | GNU | INT71 | VC02 | VC03 | ISO | |
Typy long long | ne | ano | ano | ano | ano | ano | ano | 6.2.5. |
Identifikßtor __func__ | ne | ne | ano | ano | ano | ne | ne | 6.4.2.2 |
Reßlnß Φ. hexadecimßln∞ | ne | ne | ne | ano | ano | ne | ne | 6.4.4.2 |
Jedno°ßdkov² komentß° | ano | ano | ano | ano | ano | ano | ano | 6.4.9. |
StrukturovΘ literßly | ne | ne | ano | ano | ano | ne | ne | 6.5.2.5 |
Modifikßtor restrict | ne | ne | ano | ne | ano | ne | ne | 6.7.3. |
Funkce inline | __inline | __inline | ano | ano | ano | __inline | __inline | 6.7.4. |
Nekonstantnφ meze polφ | ne | ne | ano (ne sizeof, hroutφ se p°ekladaΦ) | ano | ano | ne | ne | 6.7.5.2 |
StatickΘ meze polφ | ne | ne | ignoruje | ne | ignoruje | chyba | ignoruje | 6.7.5.2 |
Nedeklarovanß fce | varovßnφ | varovßnφ | chyba | chyba | varovßnφ | toleruje | toleruje | 6.7.5.3 |
PojmenovanΘ inicializßtory | ne | ne | ano | ne | ano | ne | ne | 6.7.8. |
Deklarace mezi p°φkazy | ne | ne | ano | ano | ano | ne | ne | 6.8 |
Implicitnφ int | toleruje | toleruje | varovßnφ | chyba | varovßnφ | toleruje | toleruje | 6.9.1. |
typedef funkce | toleruje | toleruje | chyba | chyba | chyba | chyba | chyba | 6.9.1. |
Makra s v²pustkou | ne | ne | ano | ano | ano | ne | ne | 6.10.3. |
Typy _Complex | ne | ne | ano | ano (?) | ano (?) | ne | ne | 7.3 |
hlaviΦka <fenv.h> | ne | ne | ano | ano | ne | ne | ne | 7.6 |
Makro va_copy | ne | ne | zhroutφ se p°ekladaΦ | ano | ne | ne | ne | 7.15. |
HlaviΦka <stdbool.> | ne | ne | ano | ano | ne (typ_Bool ano) | ne | ne | 7.16. |
HlaviΦka <stdint.h> | ne | ano | ano | ano | ne | ne | ne | 7.18. |
JednotlivΘ polo₧ky v nφ jsou se°azeny podle po°adφ, v
jakΘm jsou ve standardu uvedeny.
Zde projdeme jednotlivΘ body v po°adφ, ve kterΘm jsou
uvedeny v tabulce 1. (╚φslo za nadpisem odkazuje na p°φsluÜn² oddφl
standardu [1].)
Rysy jazyka C99, kterΘ nejsou zßvislΘ jen na hlaviΦkov²ch
souborech, jsou v BCXP dostupnΘ pouze p°i p°ekladu s nastaven²m p°epφnaΦem
ûC99; v INT71 je t°eba pou₧φt p°epφnaΦe /Qc99 a Qrestrict.
Je takΘ t°eba p°ipomenout, ₧e p°ekladaΦ INT71 vyu₧φvß
hlaviΦkovΘ soubory n∞kterΘho z p°ekladaΦ∙ firmy Microsoft (podle toho,
kde ho instalujeme). My jsme ho instalovali spolu s VC03.
VÜechny zkoumanΘ p°ekladaΦe umo₧≥ujφ deklarovat prom∞nnΘ
t∞chto typ∙. Umo₧≥ujφ i zapsat literßly s p°φponami LL nebo LLU. Ve vÜech
p°φpadech jde o 64bitovß celß Φφsla, kterß se ve starÜφch p°ekladaΦφch
objevovala jako rozÜφ°enφ pod oznaΦenφm __int64, resp. unsigned __int64.
Standard ovÜem takΘ zavßdφ specifikace "%lld",
resp. "%llu" pro vstup a v²stup t∞chto hodnot pomocφ funkcφ scanf(),
printf() a dalÜφch. Äßdn² z porovnßvan²ch p°ekladaΦ∙ ji ale
nepodporuje; ve vÜech p°φpadech je t°eba pou₧φt nestandardnφ specifikaci
"%I64d", resp. "%I64u", zavedenou p∙vodn∞ jako rozÜφ°enφ
pro typy __int64. Pouze p°ekladaΦ BCX p°ipouÜtφ v p°φpad∞ v²stupu takΘ
specifikaci "%LLd", kterß ovÜem takΘ postrßdß oporu ve standardu.
V ka₧dΘ funkci by m∞l b²t podle novΘho standardu definovßn
identifikßtor
static
const char __func__[] = "jmΘno_funkce";
Jde o pohodln² nßstroj, kter² by m∞lo mj. vyu₧φvat i znßmΘ
ladicφ makro assert(). Jak u₧ vφme, z porovnßvan²ch p°ekladaΦ∙ ho
implementujφ pouze BCXP, INT71 a GNU; ale GNU a INT71 ho nevyu₧φvß v makru
assert(). To znamenß, ₧e pokud aserce neplatφ, pouze BCXP vypφÜe jmΘno
funkce, v nφ₧ k chyb∞ doÜlo.
Ve zdrojovΘm textu bychom m∞li mφt mo₧nost zapisovat reßlnß
Φφsla v ÜestnßctkovΘ soustav∞ ve tvaru nap°. 0xA23.4AP-12. Zde P oznaΦuje
exponentovou Φßst Φφsla, ovÜem vzta₧enou k zßkladu 2, nikoli 10 nebo 16.
Tuto mo₧nost nabφzφ pouze p°ekladaΦ INT71.
Standard takΘ po₧aduje, abychom mohli reßlnß Φφsla v ÜestnßctkovΘm
tvaru vypsat, a to pomocφ konverzφ a, resp. A. Tuto mo₧nost ₧ßdn² z p°ekladaΦ∙
neimplementuje.
Komentß°e zaΦφnajφcφ dv∞ma lomφtky a konΦφcφ na
konci °ßdku se v p°ekladaΦφch jazyka C objevujφ jako rozÜφ°enφ ji₧
velice dlouho, a nikoho nep°ekvapφ, ₧e ho implementujφ vÜechny p°ekladaΦe,
o nich₧ zde hovo°φme.
Tato konstrukce z C99 zapl≥uje pom∞rn∞ nepochopitelnou
mezeru ve vyjad°ovacφch prost°edcφch starÜφch verzφ jazyka C a v jistΘm
smyslu se nahrazuje konstruktory z C++. Umo₧≥uje toti₧ zapsat v programu
literßl (p°φmo zapsanou konstantu) p°edstavujφcφ strukturu nebo pole. Podφvejme
se na jednoduch² p°φklad, ve kterΘm p°edpoklßdßme, ₧e x je prom∞nnß
typu Bubu:
struct Bubu {int x;} x;
// ...
x = (struct Bubu){1};
// x je prom∞nnß typu Bubu
int *p = (int[3]){1,2,3};
Tuto mo₧nost podporujφ pouze p°ekladaΦe BCPX, INT71 a GNU.
Modifikßtor restrict by m∞lo b²t mo₧nΘ pou₧φt v
deklaraci ukazatele, nap°. zßpisem
int * restrict p;
Specifikuje, ₧e vztah mezi ukazatelem a prom∞nnou, na
kterou tento ukazatel ukazuje, je v jistΘm smyslu omezen û zhruba °eΦeno,
₧e tuto prom∞nnou nebudeme m∞nit pomocφ ₧ßdnΘho dalÜφho ukazatele. To mß
p°ekladaΦi umo₧nit ÜirÜφ optimalizace.
Pou₧itφ tohoto modifikßtoru dovolujφ pouze p°ekladaΦe
BCXP a INT71; vzhledem k tomu, ₧e BCXP zatφm nepodporuje optimalizace, nemß
jeho pou₧itφ v n∞m zattφm ₧ßdn² skuteΦn² v²znam.
V²znam tohoto modifikßtoru je v podstat∞ stejn² jako v
C++ û to znamenß, ₧e p°edstavuje nßstroj pro äruΦnφô optimalizaci.
Standard ne°φkß, jak se mß funkce s tφmto modifikßtorem p°elo₧it, pouze
naznaΦuje, ₧e by v²sledn² k≤d m∞l b²t co nejrychlejÜφ.
DneÜnφ p°ekladaΦe poskytujφ modifikßtor __inline se
stejn²m v²znamem. Pouze p°ekladaΦe BCXP, INT71 a GNU znajφ standardem po₧adovan²
tvar inline.
Podle novΘho standardu lze v p°φpad∞ lokßlnφch
nestatick²ch polφ zadat poΦet prvk∙, a tedy i hornφ mez indexu, nekonstantnφm
v²razem. To znamenß, ₧e nßsledujφcφ deklarace je v C99 sprßvnß:
void f(int n)
{
int
A[n];
// Lze v C99
// ...
printf("%d", sizeof(A));
}
Zde se bude za b∞hu vyhodnocovat takΘ operßtor sizeof.
Tuto mo₧nost nabφzφ pouze p°ekladaΦe INT71 a GNU. V BCXP
lze sice takΘ deklarovat pole s nekonstantnφ hornφ mezφ, pou₧ijeme-li vÜak
na n∞ operßtor sizeof, p°ekladaΦ se zhroutφ.
Standard [1] dßle °φkß, ₧e deklarujeme-li pole jako
parametr funkce, m∙₧eme urΦit, ₧e skuteΦn²m parametrem musφ b²t pole ze
zadan²m minimßlnφm poΦtem prvk∙. K tomu slou₧φ klφΦovΘ slovo static
uvedenΘ p°ed specifikacφ poΦtu prvk∙. Nap°φklad deklarace
void f(int A[static 3])
{
//
...
}
°φkß, ₧e tΘto funkci musφme p°i volßnφ p°edat jako
skuteΦn² parametr pole s nejmΘn∞ 3 prvky.
Tuto konstrukci nepodporuje ₧ßdn² z uveden²ch p°ekladaΦ∙.
BCXP, INT71 a V03 ji ignorujφ (smφme ji pou₧φt, ale velikost p°edßvanΘho
pole nekontrolujφ), ostatnφ hlßsφ chybu.
SouΦasn² standard jazyka C zakazuje volat nebo jinak pou₧φvat
funkci, kterß dosud nebyla deklarovßna. To znamenß, ₧e tradiΦnφ prvnφ
program z uΦebnic jazyka C,
main()
{
printf("hello,
world");
}
je chybn², nebo¥ zde chybφ deklarace funkce printf() nebo
vlo₧enφ hlaviΦkovΘho souboru <stdio.h>, kter² tuto deklaraci
obsahuje. (Dßle uvidφme, ₧e je chybn² i z jinΘho d∙vodu.)
P°ekladaΦe BCPX a GNU oznaΦφ pou₧itφ nedeklarovanΘ
funkce za chybu; p°ekladaΦ BC6, BCX a INT71 vypφÜφ varovßnφ, p°ekladaΦe
VC02 a VC03 je tolerujφ bez nßmitek.
Tato novinka umo₧≥uje inicializovat pouze vybranΘ slo₧ky
struktur a polφ. Lze û Φi spφÜe m∞lo by b²t mo₧nΘ û ji takΘ pou₧φt
k inicializaci kterΘkoli ze slo₧ek unie. (P°ipome≥me si, ₧e starÜφ
standard dovolovat inicializovat pouze prvnφ slo₧ku uniφ.) Cel² trik spoΦφvß
v tom, ₧e v inicializßtoru uvedeme jmΘno slo₧ky struktury Φi unie nebo
index prvku pole, pak rovnφtko a za n∞ poΦßteΦnφ hodnotu prvku.V nßsledujφcφ
ukßzce pou₧ijeme znovu strukturu Bubu, deklarovanou v jednom z p°edchozφch p°φklad∙:
struct Bubu y = {.x
= 3};
int A[10] = {1,[7]=44};
Slo₧ky, kterΘ neinicializujeme explicitn∞, budou
inicializovßny nulou, a to i v p°φpad∞ lokßlnφch automatick²ch prom∞nn²ch.
(To ale nenφ ₧ßdnß novinka, to û vzdor n∞kter²m uΦebnicφm û platilo
u₧ v p°edchozφ verzi standardu.)
PojmenovanΘ inicializßtory podporuje pouze BCXP a INT71.
Podobn∞ jako v C++, i v jazyce C nynφ smφme zapisovat
deklarace kdekoli mezi p°φkazy. Tuto mo₧nost zatφm nabφzφ pouze p°ekladaΦe
BCXP, INT71 a GNU.
Pravidlo, oznaΦovanΘ jako äimplicitnφ intô, umo₧≥ovalo
v p°edchozφch verzφch jazyka vynechßvat klφΦovΘ slovo int v deklaracφch
nßvratovΘho typu funkcφ, statick²ch prom∞nn²ch, konstant atd. Toto
pravidlo bylo v souΦasnΘ verzi odstran∞no, tak₧e nelze napsat
const n = 1;
// Nynφ Üpatn∞
Mφsto toho je t°eba napsat
const int n = 1;
TakΘ p°φklad programu äHello, worldô o n∞kolik
odstavc∙ v²Üe je nynφ Üpatn∞.
VÜechny testovanΘ p°ekladaΦe krom∞ BCXP, INT71 a GNU
implicitnφ int tolerujφ bez nßmitek; GNU ho oznaΦφ za chybu, BCXP a INT71
vypφÜφ varovßnφ.
Ve starÜφch verzφch jazyka C bylo mo₧no definovat funkci
takto:
typedef int Fun();
Fun f {/* ... */}
Novß verze jazyka C to (po vzoru C++) zakßzala.
P°ekladaΦe BC6 a BCX to tolerujφ, ostatnφ (vΦetn∞ BCXP)
to oznaΦφ za chybu. (Jde ovÜem o konstrukci, kterß do sluÜnΘho programovßnφ
nepat°φ, nebo¥ celkem zbyteΦn∞ zhorÜuje Φitelnost programu.)
Standard nynφ dovoluje deklarovat makra s prom∞nn²m poΦtem
parametr∙, kterΘ specifikujeme pomocφ v²pustky û nap°. takto:
#define ABC(a, ...) N∞jakß definice makra
S parametry, p°edan²mi pomocφ v²pustky, zachßzφme jako
s celkem pomocφ identifikßtoru __VA_ARGS__.
Tuto mo₧nost nabφzφ pouze p°ekladaΦe BCXP, INT71 a GNU.
Jazyk C99 podle musφ implementovat t°i datovΘ typy pro
reprezentaci komplexnφch Φφsel, a to float _Complex, double _Complex a long
double _Complex. Spolu s nimi musφ poskytnout hlaviΦkov² soubor <complex.h>,
je₧ bude obsahovat mj. makro I vyjad°ujφcφ imaginßrnφ jednotku, deklarace
funkcφ pro prßci s komplexnφmi Φφsly atd.
KlφΦovΘ slovo _Complex a aritmetickΘ operace s komplexnφmi
Φφsly implementujφ pouze p°ekladaΦe BCXP, INT71 a GNU.
GNU a INT71 vÜak nenabφzφ hlaviΦkov² soubor <complex.h>,
tak₧e makro I a dalÜφ nßstroje si v n∞m obsa₧enΘ musφme implementovat
sami. (Nenφ to t∞₧kΘ û lze pou₧φt nap°. unii pole dvou reßln²ch Φφsel
odpovφdajφcφho typu a jednoho komplexnφho Φφsla.) Zde je vÜak t°eba p°ipomenout,
₧e INT71 vyu₧φvß hlaviΦkovΘ soubory microsoftsk²ch p°ekladaΦ∙.
Pod äv²poΦetnφm prost°edφmô se v tΘto souvislosti
rozumφ nßstroje, kterΘ upravujφ chovßnφ procesoru p°i prßci s reßln²mi
Φφsly û nap°. zda se zaokrouhluje v₧dy k nejbli₧Üφmu zobrazitelnΘmu Φφslu,
v₧dy k nule apod. Na PC jde v podstat∞ o p°φstup ke stavovΘmu a °φdicφmu
slovu matematickΘho koprocesoru.
Tyto nßstroje jsou soust°ed∞ny v hlaviΦkovΘm souboru
<fenv.h>. Poskytujφ je pouze p°ekladaΦe BCXP a GNU.
Makro va_copy() by m∞lo b²t definovßno v hlaviΦkovΘm
souboru <stdarg.h> a slou₧it ke kopφrovßnφ hodnoty typu va_list, je₧
se pou₧φvß pro prßci s parametry p°edßvan²mi pomocφ v²pustky. (Typ va_list
je zpravidla typedef pro ukazatel na znakovΘ nebo celoΦφselnΘ prom∞nnΘ, a
proto se v∞tÜina implementacφ jazyka C bez tohoto makra dosud obeÜla. Z°ejm∞
se vÜak vyskytujφ implementace, v nich₧ se pou₧φvß jin² mechanizmus p°φstupu
k parametr∙m na mφst∞ v²pustky.)
Toto makro implementuje pouze p°ekladaΦ GNU. P°ekladaΦ
BCXP se p°i pou₧itφ tohoto makra zhroutφ.
Jazyk C podle novΘ normy obsahuje typ _Bool reprezentujφcφ
logickΘ hodnoty. Je to celoΦφseln² typ s hodnotami 0 a 1 a v hlaviΦkovΘm
souboru <stdbool.h> jsou #definovßna makra true a false se z°ejm²m v²znamem
a n∞kterß dalÜφ.
Tento standardnφ hlaviΦkov² soubor poskytujφ pouze p°ekladaΦe
BCXP a GNU. Typ _Bool najdeme i v INT71, ovÜem bez podp∙rnΘho hlaviΦkovΘho
souboru.
Tato hlaviΦka poskytuje alternativnφ p°φstup k celoΦφseln²m
typ∙m. Najdeme v nφ °adu deklaracφ typedef, je₧ definujφ typy jako
int16_t, uint64_t apod., kterΘ umo₧≥ujφ volit datov² typ podle rozsahu, dßle
typy jako int_least32_t se zaruΦen²m minimßlnφm rozsahem, typ intptr_t, do n∞ho₧
lze ulo₧it ukazatel a zφskat ho zp∞t beze ztrßty informace, typ ptrdiff_t
pro rozdφl dvou ukazatel∙ atd.
Tento hlaviΦkov² soubor najdeme v p°ekladaΦφch BCX, BCXP
a GNU; zb²vajφcφ p°ekladaΦe ho neposkytujφ.
K tomu je t°eba poznamenat, ₧e standard takΘ zavßdφ
modifikßtory dΘlky z, resp. t pro tisk hodnot typ∙ size_t, resp. ptrdiff_t
pomocφ funkcφ z rodiny printf(). Ty neimplementuje ani jeden z uveden²ch p°ekladaΦ∙.
Je z°ejmΘ, pokud jde o implementaci novinek v jazyce C, je
situace v p°φpad∞ prakticky pou₧iteln²ch p°ekladaΦ∙ pom∞rn∞ neut∞Üenß
û ₧ßdn² z p°ekladaΦ∙ je neimplementuje v plnΘm rozsahu. To ovÜem m∙₧e
b²t d∙sledek skuteΦnosti, ₧e v∞tÜina dodavatel∙ p°ekladaΦ∙ jazyka C++
se soust°edφ spφÜe na shodu se standardem C++ a jazyk C je pro n∞ jen
jakousi nutnou p°φt∞₧φ.
Zde je, jak jsme si u₧ °ekli, situace mnohem veselejÜφ;
to ale neznamenß, ₧e je doopravdy dobrß. V Φlßnku Sedm kohout∙ na smetiÜti v tiÜt∞nΘm Chipu jsme pro porovnßnφ
zvolen²ch p°ekladaΦ∙ pou₧ili nßsledujφcφ rysy jazyka C++:
Zde se podφvßme jeÜt∞ na dalÜφ osv∞dΦenß äbolavß
mφstaô p°ekladaΦ∙. Jsou to:
Poslednφ dva body jsou podobnΘ jako v p°φpad∞ jazyka
C99. Tabulka 2 obsahuje v²sledky pro vÜechny porovnßvanΘ p°ekladaΦe.
BC6 | BCX | BCXP | GNU | INT71 | VC02 | VC03 | ISO | |
KlφΦovß slova iso646 | <iso646> | <iso646> | <iso646> | ano | <iso646> | <iso646> | <iso646> | 2.11 |
Koenigovo vyhledßvßnφ | ano | ano | jen op. | ano | ano | jen oper. | ano | 3.4.2. |
Obor deklarace v if | ano (?) | ano (?) | ano | ne | ano | ne | ano | 6.4 |
Implicitnφ int | toleruje | toleruje | toleruje | chyba | varovßnφ | toleruje | toleruje | 7.1 |
Typedef funkce | toleruje | toleruje | chyba | chyba | chyba | chyba | 8.3.5. | |
Exportnφ Üablony | ne | ne | ano | ne | ne | ne | ne | 14. |
Vno°enΘ Üablony | ano | ano | ano | ano | ano | jen inline | ano | 14.5.2. |
Parcißlnφ specializace | ano | ano | ano | ano | ano | ne | ano | 14.5.4. |
Parcißlnφ °azenφ | ano | ano | ano | ano | ano | ne | ano | 14.5.5.2 |
Specifikace throw | ano | ano | ano | ano | ne | ne | ne | 15.4 |
uncaught_exception | ano | ano | ne | ano | ano | ne | ano | 18.6.4. |
JednotlivΘ polo₧ky v nφ jsou op∞t se°azeny podle po°adφ,
v jakΘm jsou ve standardu uvedeny.
I tentokrßt budeme postupovat podle po°adφ, v n∞m₧ jsou
jednotlivΘ body uvedeny v tabulce 2. ╚φsla za nadpisy odkazujφ tentokrßt na
odpovφdajφcφ pasß₧ standardu [2].
Zdaleka ne vÜechny nßrodnφ klßvesnice obsahujφ znaky
&, | atd. Proto nabφzφ standard [2] mo₧nost alternativnφho vyjßd°enφ
operßtor∙ &&, || a aj. pomocφ klφΦov²ch slov and, or a dalÜφch.
Tuto mo₧nost poskytuje pouze p°ekladaΦ GNU. V ostatnφch
musφme postupovat stejn∞ jako v jazyce C û pou₧φt hlaviΦkov² soubor
<iso646.h>, kter² umo₧≥uje nßhradu t∞chto klφΦov²ch slov makry.
Jako Koenigovo vyhledßvßnφ
se oznaΦuje vyhledßvßnφ nekvalifikovan²ch jmen p°etφ₧en²ch operßtor∙
a funkcφ v prostorech jmen jejich parametr∙, i kdy₧ tyto prostory jmen nebyly
zp°φstupn∞ny deklaracφ nebo direktivou using. To znφ mo₧nß slo₧it∞, ale
jde v podstat∞ o jednoduchΘ pravidlo, kterΘ si nejlΘpe ukß₧eme na nßsledujφcφm
p°φkladu.
namespace Alfa
{
struct X{};
void f(X){}
X operator+(X
x, X y) {return y;}
}
int main( )
{
Alfa::X a, b,
c;
f(a);
c = a+b;
return 0;
}
Funkce main() le₧φ mimo jak²koli prostor jmen. Funkci f()
a operßtor + p°etφ₧en² pro typ Alfa::X jsme ve funkci main() pou₧ili bez
kvalifikace identifikßtorem prostoru jmen. P°esto se program p°elo₧φ a z
funkce main() zavolß opravdu Alfa::f() a Alfa::operator+(), nebo¥ p°ekladaΦ
bude jmΘna t∞chto funkcφ a operßtor∙ hledat nejen v kontextu pou₧itφ (tj.
zde mimo prostory jmen), ale i v kontextu operand∙, zde tedy v prostoru jmen
Alfa.
P°ekladaΦe BCXP a VC02 implementujφ Koenigovo vyhledßvßnφ
pouze pro operßtory (tak₧e volßnφ funkce f() v p°edchozφm p°φkladu oznaΦφ
za chybnΘ); zb²vajφcφ p°ekladaΦe ho implementujφ v souladu se standardem.
V podmφnce p°φkazu if, stejn∞ jako v podmφnce p°φkaz∙
while, smφme deklarovat prom∞nnou. Tuto prom∞nnou vÜak nesmφme p°edefinovat
ve vno°en²ch blocφch nejvyÜÜφ ·rovn∞. To znamenß, ₧e nap°. konstrukce
if(int i = f()){ int i = g(); /* ... */ }
je nesprßvnß, zatφmco konstrukce
if(int i = f()){{
int i = g(); /* ... */ }}
u₧ je p°φpustnß, nebo¥ opakovanß deklarace prom∞nnΘ i
je ve vno°enΘm bloku ni₧Üφ ·rovn∞. Podobnß omezenφ platφ i pro p°φkaz
while.
Chybnou deklaraci prom∞nnΘ v p°φkazech if a while ohlßsφ
vÜechny testovanΘ p°ekladaΦe krom∞ GNU a VC02.
K tomu je ale t°eba poznamenat, ₧e p°ekladaΦe BC6, BCX a
BCXP oznaΦφ za chybnou i sice nesmyslnou, ale syntakticky sprßvnou konstrukci
if(int i = f()){{
int i = g(); }}
(TakovΘhle v∞ci mohou v
programu vzniknout, jestli₧e hledßte chybu a postupn∞ odstra≥ujete Φßsti
k≤du, o nich₧ vφte, ₧e jsou v po°ßdku.)
O tomto pravidlu jsme ji₧ hovo°ili v souvislosti s jazykem
C99. V jazyce C++ bylo zakßzßno podstatn∞ d°φve ne₧ v jazyce C, u₧ v poΦßteΦnφch
fßzφch p°φpravy standardu; to ale neznamenß, ₧e to p°ekladaΦe dodr₧ujφ.
Pouze p°ekladaΦ GNU ohlßsφ chybu a p°ekladaΦ INT71 ohlßsφ varovßnφ,
ostatnφ ho tolerujφ bez nßmitek. Chovßnφ p°ekladaΦe INT71 je z°ejm∞
nejrozumn∞jÜφ, nebo¥ implicitnφ int byl dlouhß lΘta souΦßstφ cΘΦka°skΘho
folkl≤ru.
TakΘ o tomto pravidle jsme ji₧ hovo°ili v souvislosti s
jazykem C a takΘ tento zßkaz platil v C++ ji₧ poΦßteΦnφch fßzφch p°φpravy
standardu. P°ekladaΦe BC6 a BCX tuto odchylku od standardu tolerujφ, ostatnφ
ji zakazujφ.
Jde o jednu z mßla souΦßstφ standardu jazyka C++, kterß
nevznikla z iniciativy u₧ivatel∙, tohoto jazyka, ale jako v²sledek prßce
standardizaΦnφ komise. Jde takΘ o jednu z nejproblematiΦt∞jÜφch souΦßstφ,
a proto ji dodnes v∞tÜina p°ekladaΦ∙ neimplementuje.
Nenφ tedy divu, ₧e exportnφ Üablony neimplementuje ₧ßdn²
z porovnßvan²ch p°ekladaΦ∙. V dokumentaci k BCXP se o nich sice hovo°φ,
integrovanΘ v²vojovΘ prost°edφ toto klφΦovΘ slovo rozeznßvß, ale p°ekladaΦ
ho odmφtß.
Deklarace Üablony m∙₧e b²t vno°ena do deklarace t°φdy
nebo Üablony t°φdy. P°itom v obklopujφcφ Üablon∞ m∙₧eme uvΘst pouze
hlaviΦku Üablony metody, m∙₧eme ji tam ale takΘ zapsat celou. V nßsledujφcφm
p°φkladu jsou sprßvnΘ ob∞ mo₧nosti:
template <typename T> class Alfa
{
public:
template <typename
T1> bool F();
template <typename
T2> int G(T2 x)
{
return
(int)x;
}
};
template <typename T>
template <typename T1> bool Alfa<T>::F()
{
return true;
}
èablona metody F() je definovßna mimo t∞lo Üablony
Alfa<T>, Üablona metody G() je definovßna uvnit°.
P°ekladaΦ VC02 dovoluje pouze druhou mo₧nost û definice
vno°enΘ Üablony musφ b²t vno°ena do definice obklopujφcφ Üablony.
Ostatnφ p°ekladaΦe implementujφ vno°enΘ Üablony v plnΘm rozsahu.
Parcißlnφ specializace Üablon t°φd umo₧≥uje definovat
zvlßÜtnφ implementaci Üablony objektovΘho typu pro urΦitΘ hodnoty
parametr∙. Nejprve musφme definovat obecnou Üablonu platnou pro vÜechny
hodnoty parametr∙, pro n∞₧ nestanovφme v²jimku pomocφ parcißlnφ
specializace, pak mohou nßsledovat specializovanΘ Üablony. Nap°φklad takto:
// Obecnß Üablona
template <typename T, int N>
class Beta
{
public:
int
f(){}
};
// Parcißlnφ specializace pro N==0
template <typename T>
class Beta<T, 0>
{
public:
void
f(){}
void
g(){}
};
P°ekladaΦ pou₧ije v₧dy nejspecializovan∞jÜφ verzi Üablony,
tj. tu, jejφ₧ formßlnφ parametry nejvφce odpovφdajφ typ∙m a hodnotßm v
deklaraci.
Parcißlnφ specializaci podporujφ vÜechny porovnßvanΘ p°ekladaΦe
s v²jimkou VC02.
Pro Üablony obyΦejn²ch funkcφ (tj. funkcφ, kterΘ nejsou
metodami objektov²ch typ∙) nelze pou₧φt mechanizmus parcißlnφ
specializace. Lze je vÜak p°et∞₧ovat a p°itom pou₧φt mechanizmu parcißlnφho
°azenφ: p°ekladaΦ pou₧ije tu ze Üablon, jejφ₧ parametry nejvφce odpovφdajφ
skuteΦn²m parametr∙m zadan²m p°i volßnφ funkce. Podφvejme se na p°φklad:
template<typename T> void f(T a){}
// (1)
template<typename T> void f(T* a){}
// (2)
template<typename T> void f(const T* a){} // (3)
int main()
{
int a = 5;
int *p =
&a;
const int *cp =
p;
f(a);
// volß (1)
f(p);
// volß (2)
f(cp);
// volß (3)
return 0;
}
Tento mechanizmus implementujφ vÜechny porovnßvanΘ p°ekladaΦe
s v²jimkou VC02.
V deklaraci funkce lze specifikovat typ v²jimek, kterΘ se z
nφ mohou rozÜφ°it. K tomu slou₧φ klφΦovΘ slovo throw zapsanΘ za hlaviΦku
funkce, za nφm₧ nßsleduje v zßvorkßch seznam dovolen²ch typ∙ v²jimek. Prßzdn²
seznam znamenß, ₧e se z danΘ funkce nesmφ rozÜφ°it ₧ßdnß v²jimka;
vynechanß specifikace znamenß, ₧e se z funkce smφ rozÜφ°it jakßkoli v²jimka.
Typ v²jimky se kontroluje za b∞hu; rozÜφ°φ-li se z
funkce v²jimka nedovolenΘho typu, zavolß program standardnφ funkci
unexpected(), kterß typicky ukonΦφ program.
P°ekladaΦ INT71 dovoluje tuto specifikaci pou₧φt,
ignoruje ji vÜak. VC02 a VC03 dovolujφ tyto specifikace pou₧φt, vyu₧ijφ vÜak
pouze specifikaci throw(), je₧ °φkß, ₧e se z funkce nesmφ rozÜφ°it ₧ßdnß
v²jimka.
Tato funkce by m∞la um∞t urΦit, zda byla volßna za
änormßlnφch okolnostφô nebo p°i ·klidu zßsobnφku v dob∞ mezi vznikem
v²jimky a jejφm zachycenφm vhodn²m handlerem.
Implementujφ ji vÜechny porovnßvanΘ p°ekladaΦe; ovÜem
v BCXP a VC02 (a takΘ INT71, je-li instalovßn spolu s VC02) vracφ v₧dy false,
tak₧e ji nelze vyu₧φt.
V tomto porovnßvßnφ vynechßme p°ekladaΦ BCXP, nebo¥
mj. nenabφzφ mo₧nost optimalizace p°elo₧enΘho k≤du. Vedle toho trpφ °adou
dalÜφch nedostatk∙ û nap°. hlaviΦkov² soubor <sys\timeb.h>
obsahuje deklaraci funkce ftime(), kterß v knihovn∞ chybφ, p°i pou₧itφ n∞kter²ch
konstrukcφ se p°ekladaΦ hroutφ atd.
Pro porovnßnφ jsme pou₧ili nßsledujφcφ programy, kterΘ
ukazujφ r∙znΘ strßnky optimalizace û prßci s reßln²mi Φφsly, prßci s
pam∞tφ, efektivitu vstupnφch a v²stupnφch operacφ atd.
Jde o klasickou kombinatorickou ·lohu, kterou formuloval n∞meck²
matematik K. F. Gauss n∞kdy p°ed rokem 1850. Cφlem je rozmφstit n
dam na Üachovnici s n ╫ n
poli tak, aby se navzßjem neohro₧ovaly. (To nemß nic spoleΦnΘho se Üachem,
p°ipomφnß to spφÜe skuteΦn² ₧ivot).
Snadno zjistφte, ₧e pro Üachovnici 4 ╫ 4 mß tato ·loha pouhß 2 °eÜenφ; pro Üachovnici 8
╫ 8 jich je ji₧ 92 a pro Üachovnici 13
╫ 13 jich je p°es 70 000. NßÜ program nevypisuje jednotlivß °eÜenφ,
pouze zjiÜ¥uje jejich poΦet (nejde nßm o test vstupnφch a v²stupnφch
operacφ). Jßdrem programu je nßsledujφcφ funkce:
void vyres(int m)
{
if(m == n) PocRe++;
else
for(int
k = 0; k < n; k++)
{
if(Bezpecne(k,m))
{
X[m]
= k;
vyres(m+1);
}
}
X[m] = -1;
}
Pole X[] obsahuje polohy dam v jednotliv²ch sloupcφch Üachovnice,
funkce Bezpecne() uvracφ true, pokud lze dßmu na danΘ pole umφstit, tj.
pokud ji na n∞m ₧ßdnß z d°φve umφst∞n²ch dam neohro₧uje.
Tento program p°eΦte 30 000 reßln²ch Φφsel ze
souboru a ihned je vypφÜe na konzolu. Takto testujeme jak standardnφ vstupnφ
a v²stupnφ nßstroje z jazyka C (funkce scanf() a printf()), tak i objektovΘ
datovΘ proudy cout a cin z C++. Vstupnφ data Φteme ze souboru pomocφ p°esm∞rovßnφ,
zadanΘho v p°φkazovΘm °ßdku, a v²stup sm∞°uje na konzolu.
Tento program v podstat∞ testuje kvalitu implementace formßtovacφch
operacφ.
Samotn² vstup a v²stup obstarßvajφ slu₧by operaΦnφho
systΘmu. Mohlo by se tedy zdßt, ₧e v t∞chto operacφch nebude mezi jednotliv²mi
p°ekladaΦi ₧ßdn² zßva₧n² rozdφl. Rozdφlnost v²sledk∙, shrnut²ch v
tabulce 3, vÜak ukazuje, ₧e formßtovacφ operace zabφrajφ podstatnou Φßst
pot°ebnΘho Φasu. To znamenß, ₧e v tomto testu porovnßvßme zaprvΘ kvalitu
implementace formßtovacφch operacφ a zadruhΘ kvalitu optimalizace t∞chto
operacφ.
K otestovßnφ prßce s pam∞tφ pou₧φvßme zapl≥ovßnφ a
vyprazd≥ovßnφ spojovΘho seznamu: Do seznamu 10000krßt ulo₧φme 1000 cel²ch
Φφsel a pak tento seznam vyprßzdnφme.
K tomu pou₧φvßme vlastnφ velice
jednoduchou (a jedno·Φelovou) implementaci jednosm∞rn∞ z°et∞zenΘho
seznamu:
//
Prvek spojovΘho seznamu
class
prvek
{
long data;
prvek * dalsi;
public:
prvek(long i, prvek* d=0): dalsi(d), data(i){}
prvek* Dalsi(){ return dalsi; }
};
class
seznam
{
prvek *hlava;
public:
void vloz(long y);
seznam() : hlava(new prvek(0,0))
{
if(!hlava) Chyba();
}
~seznam(); // Vyprßzdnφ seznam
};
Kdybychom pou₧ili standardnφ Üablonu
list<T>, zßvisely by v²sledky nejen na p°ekladaΦi, ale i na pou₧itΘ
implementaci standardnφ knihovny, a tomu se zde chceme vyhnout.
Jazyky C a C++ se Φasto pou₧φvajφ i pro numerickΘ v²poΦty;
i kdy₧ v tΘto oblasti zatφm stßle dominuje Fortran, prosazuje se C a C++ Φφm
dßl vφce.
Testovacφ program °eÜφ soustavu 40 lineßrnφch
algebraick²ch rovnic o 40 neznßm²ch. Jde o soustavu, kterß vznikne p°i °eÜenφ
Poissonovy rovnice na jednotkovΘm Φtverci metodou sφtφ. Koeficienty tΘto
soustavy se pravideln∞ opakujφ, tak₧e nenφ t°eba uklßdat v pam∞ti.
K °eÜenφ jsme pou₧ili GaussovuûSeidlovu iteraΦnφ
metodu.
Jßdrem programu je nßsledujφcφ funkce IteracniKrok(),
kterß obstarß jeden iteraΦnφ krok:
double IteracniKrok()
{
double r = 0;
for(int i = 1; i < N; i++)
for(int
j = 1; j < N; j++)
{
double
d = (A[i+1][j] + A[i-1][j] +
A[i][j+1]
+ A[i][j-1])*0.25
+
sqr(s)*0.25*g(i*s, j*s);
r
+= sqr(d-A[i][j]);
A[i][j]
= d;
}
return r;
}
Tato funkce vracφ souΦet druh²ch mocnin rozdφl∙ slo₧ek
A[i][j] °eÜenφ vypoΦten²ch v tomto a v p°edchozφm kroku.
╪eÜenφ A je ulo₧eno ve dvojrozm∞rnΘm poli, nebo¥ jeho
slo₧ky p°edstavujφ hodnoty hledanΘ funkce v bodech sφt∞ polo₧enΘ na
jednotkov² Φtverec. Funkce g() p°edstavuje pravou stranu Poissonovy rovnice,
funkce sqr() poΦφtß druhou mocninu p°edanΘ hodnoty.
V²jimky by m∞ly slou₧it pouze k °eÜenφ v²jimeΦn²ch
situacφ. Z toho plyne, ₧e by jejich oÜet°ovßnφ nem∞lo zabφrat p°evß₧nou
Φßst doby b∞hu programu. P°esto je t°eba se zab²vat takΘ efektivitou
jejich implementace û kdy₧ u₧ nic jinΘho, tak proto, abychom m∞li jasnou p°edstavu,
jak jsou nßroΦnΘ. ObΦas se toti₧ lze setkat s p°edstavou, ₧e v²jimky p°edstavujφ
alternativu k b∞₧nΘmu nßvratu z funkce a ₧e by bylo vhodnΘ je takov²mto
zp∙sobem vyu₧φvat.
Testovacφ program srovnßvß volßnφ funkce, kterß vracφ
konstantnφ v²sledek b∞₧n²m zp∙sobem, a volßnφ funkce, kterß vracφ
stejn² v²sledek pomocφ v²jimky.
double pinorm()
{
return 3.141;
}
double pithr()
{
throw 3.141;
}
Tyto funkce jsou volßny 100000krßt.
Dobu pot°ebnou pro jednotlivΘ testovacφ programy jsme m∞°ili
takto:
int main()
{
/* P°φpravnΘ operace */
timeb T1, T2;
ftime(&T1);
// ZaΦßtek m∞°enφ
/* Zde je m∞°en² ·sek programu */
ftime(&T2);
// Konec m∞°enφ
// V²stup v²sledku
cout << (T2.time+T2.millitm*0.001)
û
(T1.time+T1.millitm*0.001)
return 0;
}
Funkce ftime()nenφ souΦßstφ standardu, je vÜak natolik b∞₧n²m
rozÜφ°enφm, ₧e ji implementujφ vÜechny testovanΘ p°ekladaΦe.
Tato fakta jsme uvedli ji₧ v Φlßnku Sedm kohout∙ na smetiÜti v tiÜt∞nΘm Chipu; pro pohodlφ Φtenß°e
je ale zopakujeme.
Testovacφ programy jsme spouÜt∞li na PC vybavenΘm
procesorem Athlon/1 GHz s 512 MB RAM pod operaΦnφm systΘmem Windows 2000. Ka₧d²
program jsme spustili desetkrßt jako jedinou aplikaci v dosovΘm okn∞ a
tabulka 3 ukazuje pr∙m∞ry dosa₧en²ch Φas∙ v sekundßch.
CB6 | CBX | GNU | INT71 | VC02 | VC03 | |
13 dam | 7,5 | 7,5 | 13,8 | 5,5 | 6,1 | 6,0 |
stdio | 6,6 | 3,4 | 3,2 | 3,0 | 2,6 | 3,0 |
iostream | 7,2 | 8,2 | 11,2 | 8,5 | 2,6 | 8,5 |
pam∞¥ | 3,0 | 2,9 | 7,2 | 5,3 | 5,3 | 5,0 |
soustava | 2,4 | 1,6 | 2,8 | 1,6 | 1,3 | 1,9 |
v²jimky | 0,8 | 7,4 | 1,7 | 0,6 | 0,5 | 0,6 |
I kdy₧ jsou dosa₧enΘ Φasy pom∞rn∞ vyrovnanΘ, lze z
nich zφskat p°edstavu o kvalit∞ porovnßvan²ch p°ekladaΦ∙.
Ani porovnßnφ zahrnujφcφ vφce kritΘriφ nßm neumo₧≥uje
dßt na tuto otßzku jednoznaΦnou odpov∞∩. NaÜe zßv∞ry budou stejnΘ jako
v tiÜt∞nΘm Chipu:
[1] International Standard ISO/IEC 9899:1999. Programming
Languages û C.
[2] International Standard ISO/IEC 14882:1998. Programming
Languages û C++
Miroslav Virius