Retour à l'accueil

Les using en C++ : recherche de nom et déclarations répétées

L'article analyse le comportement des directives et déclarations using en C++ : répétées dans l'espace de noms et les blocs, restrictions dans les classes, différences dans la recherche de nom. Exemples de code avec comportement inattendu sont fournis.

Pièges des using en C++ : pourquoi les noms trompent le compilateur
Advertisement 728x90

Utilisation des directives et déclarations en C++ : pièges cachés de la recherche de noms

Dans les espaces de noms, les déclarations using répétées pour le même nom sont autorisées et ne provoquent pas d'erreurs. Le compilateur ignore silencieusement les doublons, les traitant comme des répétitions inoffensives.

namespace lib {
    int value = 42;
}

namespace demo {
    using lib::value;
    using lib::value; // Valide, aucune nouvelle entité créée

    void f() {
        value = 10; // Référence à lib::value
    }
}

Cette règle a évolué depuis C++98, où une telle répétition était formellement interdite dans les blocs, mais les compilateurs (GCC, MSVC) l’acceptaient néanmoins. L’issue CWG 36 a mis à jour la norme en C++03 pour aligner le comportement avec celui des espaces de noms.

Asymétrie historique dans les blocs

Dans les fonctions ou les blocs, les déclarations using répétées pour le même nom provoquaient une erreur de compilation sous C++98 §7.3.3/9 :

Google AdInline article slot
void f() {
    using A::i;
    using A::i; // Erreur selon la norme C++98
}

La pratique des compilateurs a devancé la norme — ce code fonctionnait dans les projets réels. Le comité a reconnu cette incohérence, en comparant cette situation à la possibilité de déclarer plusieurs fois la même fonction :

void f() {
    void g();
    void g(); // Valide
}

Depuis C++03, les déclarations using répétées dans les blocs sont permises si les noms sont identiques.

Limites dans les classes

Les déclarations using dans les classes ne fonctionnent que pour les noms provenant des classes de base — pas pour ceux d'espaces de noms externes :

Google AdInline article slot
struct S {
    using lib::value; // Erreur : nom non membre dans un contexte de classe
};

Il s’agit d’une restriction fondamentale : les déclarations using n’introduisent pas directement des noms externes dans un espace de noms de classe.

Différence entre using namespace et déclaration using

La différence clé réside dans la manière dont la recherche de noms fonctionne :

  • using namespace étend la portée de recherche sans introduire de nouveaux noms dans le contexte courant.
  • using declaration introduit un nom local dans le contexte courant.

L'exemple suivant illustre un conflit :

Google AdInline article slot
namespace B { int x = 10; }
namespace C { int x = 20; }
namespace A {
    using namespace B; // Directive : étend la portée de recherche

    void f() {
        using C::x; // Déclaration : introduit x localement

        A::x = 1; // B::x (recherche qualifiée via using namespace)
        x = 2;    // C::x (recherche non qualifiée, trouve le nom local)
    }
}

À l'intérieur de f(), les deux instructions d'affectation modifient des variables différentes : A::x (qui se résout en B::x) et le x local (qui fait référence à C::x). La recherche non qualifiée s'arrête à la première correspondance dans le contexte courant.

Cas de test complet :

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
}

Implications pratiques pour les revues de code

Ces constructions rendent le code plus difficile à comprendre. Dans les fonctions complexes, les déclarations using peuvent masquer l'origine réelle des noms, entraînant des bugs à l'exécution. Recommandation : éviter using dans le code de production sauf si explicitement documenté.

Aux entretiens, 80 % des développeurs confirmés échouent à expliquer ce comportement sans debugger — ce qui révèle un manque d'expérience concrète avec la recherche de noms.

Points clés :

  • Les déclarations using répétées pour le même nom sont autorisées dans les espaces de noms et les blocs depuis C++03.
  • using dans les classes est limité aux noms provenant des classes de base.
  • using namespace étend la portée de recherche mais n'introduit pas de noms ; using declaration crée un nom local.
  • La recherche non qualifiée s'arrête à la première correspondance dans le contexte courant.
  • Ces patterns sont des indicateurs rouges lors des revues de code.

— Editorial Team

Advertisement 728x90

Lire ensuite