Pourquoi les espaces de noms sans nom sont-ils utilisés et quels sont leurs avantages?

242

Je viens de rejoindre un nouveau projet logiciel C ++ et j'essaie de comprendre la conception. Le projet utilise fréquemment des espaces de noms sans nom. Par exemple, quelque chose comme cela peut se produire dans un fichier de définition de classe:

// newusertype.cc
namespace {
  const int SIZE_OF_ARRAY_X;
  const int SIZE_OF_ARRAY_Y;
  bool getState(userType*,otherUserType*);
}

newusertype::newusertype(...) {...

Quelles sont les considérations de conception qui pourraient conduire à utiliser un espace de noms sans nom? Quels sont les avantages et les inconvénients?

Scottie T
la source

Réponses:

189

Les espaces de noms sans nom sont un utilitaire pour rendre une unité de traduction d'identifiant locale. Ils se comportent comme si vous choisissiez un nom unique par unité de traduction pour un espace de noms:

namespace unique { /* empty */ }
using namespace unique;
namespace unique { /* namespace body. stuff in here */ }

L'étape supplémentaire utilisant le corps vide est importante, vous pouvez donc déjà faire référence dans le corps de l'espace de noms à des identifiants comme ::nameceux définis dans cet espace de noms, car la directive using a déjà eu lieu.

Cela signifie que vous pouvez avoir des fonctions gratuites appelées (par exemple) helpqui peuvent exister dans plusieurs unités de traduction, et elles ne s'affronteront pas au moment de la liaison. L'effet est presque identique à l'utilisation du staticmot - clé utilisé en C que vous pouvez insérer dans la déclaration des identifiants. Les espaces de noms sans nom sont une alternative supérieure, pouvant même rendre une unité de traduction de type locale.

namespace { int a1; }
static int a2;

Les deux asont des unités de traduction locales et ne s'affronteront pas au moment du lien. Mais la différence est que a1dans l'espace de noms anonyme obtient un nom unique.

Lisez l'excellent article de comeau-computing Pourquoi un espace de noms sans nom est-il utilisé au lieu de statique? ( Miroir Archive.org ).

Johannes Schaub - litb
la source
Vous expliquez la relation avec static. Pouvez-vous également comparer avec __attribute__ ((visibility ("hidden")))?
phinz
74

Avoir quelque chose dans un espace de noms anonyme signifie qu'il est local pour cette unité de traduction (fichier .cpp et toutes ses inclusions), cela signifie que si un autre symbole portant le même nom est défini ailleurs, il n'y aura pas de violation de la règle de définition unique (ODR).

C'est la même chose que la manière C d'avoir une variable globale statique ou une fonction statique, mais elle peut également être utilisée pour les définitions de classe (et doit être utilisée plutôt qu'en staticC ++).

Tous les espaces de noms anonymes d'un même fichier sont traités comme le même espace de noms et tous les espaces de noms anonymes de différents fichiers sont distincts. Un espace de noms anonyme est l'équivalent de:

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;
Motti
la source
14

L'espace de nom sans nom limite l'accès de la classe, de la variable, de la fonction et des objets au fichier dans lequel il est défini. La fonctionnalité d'espace de noms sans nom est similaire au staticmot clé en C / C ++.
staticLe mot-clé limite l'accès de la variable globale et de la fonction au fichier dans lequel elles sont définies.
Il existe une différence entre un espace de noms sans nom et un staticmot - clé, car l'espace de noms sans nom a un avantage sur statique. staticLe mot-clé peut être utilisé avec une variable, une fonction et des objets, mais pas avec une classe définie par l'utilisateur.
Par exemple:

static int x;  // Correct 

Mais,

static class xyz {/*Body of class*/} //Wrong
static structure {/*Body of structure*/} //Wrong

Mais cela peut être possible avec un espace de noms sans nom. Par exemple,

 namespace {
           class xyz {/*Body of class*/}
           static structure {/*Body of structure*/}
  } //Correct
Sachin
la source
13

En plus des autres réponses à cette question, l'utilisation d'un espace de noms anonyme peut également améliorer les performances. Comme les symboles dans l'espace de noms n'ont besoin d'aucune liaison externe, le compilateur est plus libre pour effectuer une optimisation agressive du code dans l'espace de noms. Par exemple, une fonction qui est appelée plusieurs fois une fois dans une boucle peut être insérée sans aucun impact sur la taille du code.

Par exemple, sur mon système, le code suivant prend environ 70% du temps d'exécution si l'espace de noms anonyme est utilisé (x86-64 gcc-4.6.3 et -O2; notez que le code supplémentaire dans add_val fait que le compilateur ne veut pas inclure deux fois).

#include <iostream>

namespace {
  double a;
  void b(double x)
  {
    a -= x;
  }
  void add_val(double x)
  {
    a += x;
    if(x==0.01) b(0);
    if(x==0.02) b(0.6);
    if(x==0.03) b(-0.1);
    if(x==0.04) b(0.4);
  }
}

int main()
{
  a = 0;
  for(int i=0; i<1000000000; ++i)
    {
      add_val(i*1e-10);
    }
  std::cout << a << '\n';
  return 0;
}
xioxox
la source
5
Trop beau pour être vrai - j'ai essayé ce segment sur gcc 4-1-2, en utilisant l'optimisation O3, avec et sans l'instruction namespace: -> J'ai le même temps (3sec, avec -O3 et 4sec avec -O3)
Theo
2
Ce code était intentionnellement complexe pour essayer de persuader le compilateur de ne pas incorporer b et d'ajouter_val dans main. L'optimisation O3 utilise beaucoup d'inlining, quel que soit le coût du codage du ballonnement. Cependant, il existe toujours des fonctions où O3 n'inclurait pas add_val. Vous pouvez essayer de rendre add_val plus complexe ou de l'appeler plusieurs fois depuis main dans différentes circonstances.
xioxox
5
@Daniel: qu'est-ce qui me manque? comme lu, vous avez dit que vous vous compariez -O3à lui-même, puis vous avez dit que 3 contre 4 secondes sont "en même temps". ni l'un ni l'autre n'ont un peu de sens. je soupçonne que la véritable explication serait, mais qu'est-ce que c'est?
underscore_d
@underscore_d La réponse indique -O2 a été utilisé dans les deux cas, pas -O3. Différents niveaux d'optimisation peuvent se comporter différemment. En outre, différentes versions du compilateur peuvent se comporter différemment (la réponse peut devenir obsolète, c'est-à-dire)
Paul Stelian
1
@PaulStelian Je le sais, mais il semble assez clair que je répondais non pas à la réponse de xioxox mais plutôt au commentaire de Theo (bien que son nom ait changé ou que je me sois trompé)
underscore_d
12

L'exemple montre que les personnes du projet que vous avez rejoint ne comprennent pas les espaces de noms anonymes :)

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

Ceux-ci n'ont pas besoin d'être dans un espace de noms anonyme, car l' constobjet a déjà une liaison statique et ne peut donc pas éventuellement entrer en conflit avec des identificateurs du même nom dans une autre unité de traduction.

    bool getState(userType*,otherUserType*);
}

Et c'est en fait une pessimisation: getState()a un lien externe. Il est généralement préférable de préférer la liaison statique, car cela ne pollue pas la table des symboles. Il vaut mieux écrire

static bool getState(/*...*/);

ici. Je suis tombé dans le même piège (il y a un libellé dans la norme qui suggère que les statiques de fichiers sont en quelque sorte déconseillées en faveur des espaces de noms anonymes), mais en travaillant dans un grand projet C ++ comme KDE, vous obtenez beaucoup de gens qui tournent la tête dans le bon sens autour de nouveau :)

Marc Mutz - mmutz
la source
10
Étant donné que les espaces de noms non nommés c ++ 11 ont un lien interne (section 3.5 dans la norme ou en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces )
Emile Vrijdags
11
"Ceux-ci n'ont pas besoin d'être dans un espace de noms anonyme" Techniquement, bien sûr - mais quand même, cela ne fait pas de mal de les mettre en un, comme un rappel visuel de leur sémantique et de le rendre (encore plus) trivial pour supprimer constness plus tard si vous le souhaitez. Je doute que cela signifie que l'équipe du PO "ne comprend rien"! En outre, le bit sur les fonctions dans les espaces de noms anonymes ayant une liaison externe est incorrect dans C ++ 11 et suivants, comme indiqué. D'après ce que je comprends, ils ont résolu un problème d'arguments de modèle nécessitant auparavant une liaison externe, ce qui pouvait permettre aux espaces de noms sans nom (capables de contenir des arguments de modèle) d'avoir une liaison interne.
underscore_d
11

Un espace de noms anonyme rend les variables, fonctions, classes, etc. incluses uniquement disponibles dans ce fichier. Dans votre exemple, c'est un moyen d'éviter les variables globales. Il n'y a aucune différence de performances d'exécution ou de compilation.

Il n'y a pas tant d'avantages ou d'inconvénients que "Est-ce que je veux que cette variable, fonction, classe, etc. soit publique ou privée?"

Max Lybbert
la source
2
Il peut y avoir des différences de performances - voir ma réponse ici. Il permet au compilateur d'optimiser le code mieux.
xioxox
2
Vous avez raison; au moins en ce qui concerne C ++ aujourd'hui. Cependant, C ++ 98 / C ++ 03 nécessite que les choses aient une liaison externe afin d'être utilisées comme arguments de modèle. Étant donné que les éléments dans les espaces de noms anonymes sont disponibles en tant qu'arguments de modèle, ils auraient un lien externe (au moins dans la version antérieure à C ++ 11) même s'il n'y avait aucun moyen de s'y référer depuis l'extérieur du fichier. Je pense qu'il y a peut-être eu une certaine capacité à contourner cela, car la norme exige seulement que les choses agissent comme si les règles étaient appliquées; et il est parfois possible de le faire sans vraiment appliquer les règles.
Max Lybbert