Použití using-direktiv a deklarací v C++: skryté chytře při hledání jmen
V oborech názvů je povoleno opakované použití stejného jména pomocí using-deklarací, aniž by to způsobilo konflikt. Kompilátor tyto duplikáty ignoruje jako bezvadné opakování.
namespace lib {
int value = 42;
}
namespace demo {
using lib::value;
using lib::value; // správně, žádná nová entita
void f() {
value = 10; // lib::value
}
}
Toto pravidlo se vyvíjelo od C++98, kde byly opakování v blokových oborech formálně zakázány, ale kompilátory (GCC, MSVC) je přijímaly. Issue CWG 36 v C++03 standard upravil, aby se chování shodovalo s chováním v oborech názvů.
Blokové obory: historická nesymetrie
V funkcích nebo blokových oborech způsobovalo opakování using stejného jména dříve chybu při kompilaci podle C++98 §7.3.3/9:
void f() {
using A::i;
using A::i; // chyba v standardu 98
}
Praxe kompilátorů předběhla standard: kód fungoval. Komise uznala rozpor a porovnala to s povolenými opakovanými deklaracemi funkcí:
void f() {
void g();
void g(); // platný kód
}
Od C++03 jsou opakování v blokových oborech povoleny, pokud je jméno identické.
Omezení v třídách
Použití using v třídách funguje pouze pro jména z nadřazených tříd, nikoli z vnějších oborů názvů:
struct S {
using lib::value; // chyba: nečlenské jméno v oboru třídy
};
To je zásadní omezení: using-deklarace nepřenáší vnější jména přímo do třídy.
Rozdíl mezi using namespace a using-deklarací
Klíčový rozdíl spočívá v mechanismu hledání jmen:
- using namespace rozšiřuje hledání, ale nezavádí jména do aktuálního oboru.
- using-deklarace deklaruje jméno lokálně v daném oboru.
Příklad ukazuje konflikt:
namespace B { int x = 10; }
namespace C { int x = 20; }
namespace A {
using namespace B; // direktiva: rozšiřuje hledání
void f() {
using C::x; // deklarace: zavádí x lokálně
A::x = 1; // B::x (kvalifikované hledání prostřednictvím using namespace)
x = 2; // C::x (nezkvalifikované, lokální jméno)
}
}
Ve funkci f() dvě řádky mění různé proměnné: A::x (což je B::x) a lokální x (C::x). Nezkvalifikované hledání se zastaví na prvním shodném jménu v oboru.
Úplný test:
int main() {
A::f();
printf("A::x: %d\n", A::x); // 1
printf("B::x: %d\n", B::x); // 1
printf("C::x: %d\n", C::x); // 2
}
Praktické důsledky pro revize kódu
Takové konstrukce komplikují pochopení kódu. V velkých funkcích mohou using-deklarace zakrýt skutečné zdroje jmen a vést ke chybám během provozu. Doporučení: vyhnout se using v produkčním kódu nebo jejich použití jasně dokumentovat.
Na pohovorech 80 % středně pokročilých a senior programátorů nedokáže vysvětlit chování bez ladění — indikátor nedostatku zkušeností s hledáním jmen.
Důležité:
- Opakované using jednoho jména jsou povoleny v oborech názvů i blokových oborech od C++03.
- Using v třídách je možné pouze z nadřazených tříd.
- using namespace rozšiřuje hledání, ale nezavádí jména; using-deklarace zavádí lokální jméno.
- Nezkvalifikované hledání se zastaví na prvním shodném jménu v oboru.
- Takové vzory jsou červeným vlajkem při revizi kódu.
— Editorial Team
Zatím žádné komentáře.