Espaces de noms sans nom / anonymes vs fonctions statiques

Une fonctionnalité de C ++ est la possibilité de créer des espaces de noms sans nom (anonymes), comme ceci:

namespace { int cannotAccessOutsideThisFile() { ... } } // namespace 

Vous penseriez qu’une telle fonctionnalité serait inutile – puisque vous ne pouvez pas spécifier le nom de l’espace de nom, il est impossible d’y accéder depuis l’extérieur. Mais ces espaces de noms non nommés sont accessibles dans le fichier dans lequel ils ont été créés, comme si vous aviez une clause d’utilisation implicite.

Ma question est la suivante: pourquoi ou quand serait-il préférable d’utiliser des fonctions statiques? Ou sont-ils essentiellement deux manières de faire exactement la même chose?

La norme C ++ se lit à la section 7.3.1.1 Espaces de nommage sans nom, paragraphe 2:

L’utilisation du mot clé static est déconseillée lors de la déclaration d’objects dans une scope d’espace de nommage, l’espace de nommage sans nom offre une alternative supérieure.

Statique ne s’applique qu’aux noms d’objects, fonctions et unions anonymes, pas à saisir les déclarations.

Modifier:

La décision de déprécier cette utilisation du mot-clé static (affectant la visibilité d’une déclaration de variable dans une unité de traduction) a été inversée ( ref ). Dans ce cas, l’utilisation d’un espace de noms statique ou non nommé revient essentiellement à deux manières de faire exactement la même chose. Pour plus d’informations, veuillez consulter cette question SO.

Les espaces de nommage sans nom ont toujours l’avantage de vous permettre de définir des types locaux de traduction. S’il vous plaît voir cette question SO pour plus de détails.

Nous remercions Mike Percy d’avoir attiré mon attention.

Placer des méthodes dans un espace de noms anonyme vous empêche de violer accidentellement la règle de définition unique , ce qui vous permet de ne jamais vous soucier de nommer vos méthodes d’aide de la même manière qu’une autre méthode que vous pouvez lier.

Et, comme le fait remarquer Luke, les espaces de noms anonymes sont préférés par les standards aux membres statiques.

Il y a un cas particulier où la statique a un effet surprenant (du moins c’était pour moi). Le standard C ++ 03 indique en 14.6.4.2/1:

Pour un appel de fonction qui dépend d’un paramètre de modèle, si le nom de la fonction est un identificateur non qualifié mais pas un identifiant de modèle , les fonctions candidates sont trouvées en utilisant les règles de recherche habituelles (3.4.1, 3.4.2), sauf que:

  • Pour la partie de la recherche utilisant la recherche de nom non qualifiée (3.4.1), seules les déclarations de fonction avec un lien externe provenant du contexte de définition de modèle sont trouvées.
  • Pour la partie de la recherche utilisant des espaces de noms associés (3.4.2), seules les déclarations de fonction avec un lien externe trouvées dans le contexte de définition du modèle ou dans le contexte d’instanciation du modèle sont trouvées.

Le code ci-dessous appellera foo(void*) et non foo(S const &) comme prévu.

 template  int b1 (T const & t) { foo(t); } namespace NS { namespace { struct S { public: operator void * () const; }; void foo (void*); static void foo (S const &); // Not considered 14.6.4.2(b1) } } void b2() { NS::S s; b1 (s); } 

En soi, ce n’est probablement pas une grosse affaire, mais cela souligne que pour un compilateur C ++ totalement compatible (c’est-à-dire avec un support pour l’ export ), le mot-clé static aura toujours des fonctionnalités qui ne sont pas disponibles.

 // bar.h export template  int b1 (T const & t); // bar.cc #include "bar.h" template  int b1 (T const & t) { foo(t); } // foo.cc #include "bar.h" namespace NS { namespace { struct S { }; void foo (S const & s); // Will be found by different TU 'bar.cc' } } void b2() { NS::S s; b1 (s); } 

La seule façon de s’assurer que la fonction dans notre espace de noms sans nom ne sera pas trouvée dans les modèles utilisant ADL est de la rendre static .

Mise à jour pour Modern C ++

A partir de C ++ ’11, les membres d’un espace de noms sans nom ont un lien interne implicite (3.5 / 4):

Un espace de noms sans nom ou un espace de noms déclaré directement ou indirectement dans un espace de noms sans nom a un lien interne.

Mais en même temps, le 14.6.4.2/1 a été mis à jour pour supprimer la mention du lien (extrait de C ++ ’14):

Pour un appel de fonction où l’expression postfixe est un nom dépendant, les fonctions candidates sont trouvées en utilisant les règles de recherche habituelles (3.4.1, 3.4.2), sauf que:

  • Pour la partie de la recherche utilisant la recherche de nom non qualifiée (3.4.1), seules les déclarations de fonction du contexte de définition de modèle sont trouvées.

  • Pour la partie de la recherche utilisant des espaces de noms associés (3.4.2), seules les déclarations de fonction trouvées dans le contexte de définition du modèle ou dans le contexte d’instanciation du modèle sont trouvées.

Le résultat est que cette différence particulière entre les membres d’espace de noms statiques et non nommés n’existe plus.

J’ai récemment commencé à remplacer les mots-clés statiques par des espaces de noms anonymes dans mon code, mais j’ai immédiatement rencontré un problème où les variables de l’espace-noms n’étaient plus disponibles pour inspection dans mon débogueur. J’utilisais VC60, donc je ne sais pas s’il s’agit d’un problème avec d’autres débogueurs. Ma solution de contournement consistait à définir un espace de noms “module”, où je lui ai donné le nom de mon fichier cpp.

Par exemple, dans mon fichier XmlUtil.cpp, je définis un espace de noms XmlUtil_I {…} pour toutes les variables et fonctions de mon module. De cette façon, je peux appliquer la qualification XmlUtil_I :: dans le débogueur pour accéder aux variables. Dans ce cas, le «_I» le distingue des espaces de noms publics tels que XmlUtil que je souhaiterais utiliser ailleurs.

Je suppose qu’un inconvénient potentiel de cette approche par rapport à une approche véritablement anonyme est que quelqu’un pourrait violer l’étendue statique souhaitée en utilisant le qualificatif d’espace de noms dans les autres modules. Je ne sais pas si c’est une préoccupation majeure cependant.

L’utilisation du mot clé statique à cette fin est déconseillée par la norme C ++ 98. Le problème avec le statique est qu’il ne s’applique pas à la définition de type. C’est également un mot-clé surchargé utilisé de différentes manières dans différents contextes. Les espaces de noms sans nom simplifient donc un peu les choses.

D’expérience, je noterai simplement que bien qu’il s’agisse du moyen C ++ de placer des fonctions précédemment statiques dans l’espace de noms anonyme, les compilateurs plus anciens peuvent parfois avoir des problèmes avec cela. Je travaille actuellement avec quelques compilateurs pour nos plates-formes cibles, et le compilateur Linux plus moderne permet de placer des fonctions dans l’espace de noms anonyme.

Mais un compilateur plus ancien fonctionnant sous Solaris, auquel nous sums attachés jusqu’à une prochaine version non spécifiée, l’acceptera parfois, et d’autres fois le signaleront comme une erreur. L’erreur n’est pas ce qui m’inquiète, c’est ce qu’elle pourrait faire lorsqu’elle l’ accepterait . Donc, tant que nous ne sums pas modernes, nous utilisons toujours des fonctions statiques (généralement classées) où nous préférons les espaces de noms anonymes.

De plus, si on utilise un mot-clé statique sur une variable comme cet exemple:

 namespace { static int flag; } 

Il ne serait pas visible dans le fichier de mappage

Ayant appris cette fonctionnalité tout à l’heure en lisant votre question, je ne peux que spéculer. Cela semble offrir plusieurs avantages par rapport à une variable statique au niveau fichier:

  • Les espaces de noms anonymes peuvent être nesteds les uns dans les autres, offrant plusieurs niveaux de protection contre lesquels les symboles ne peuvent pas s’échapper.
  • Plusieurs espaces de noms anonymes peuvent être placés dans le même fichier source, créant ainsi différentes étendues de niveau statique dans le même fichier.

Je serais intéressé d’apprendre si quelqu’un a utilisé des espaces de noms anonymes en code réel.

Une différence spécifique au compilateur entre les espaces de noms anonymes et les fonctions statiques peut être vue en compilant le code suivant.

 #include  namespace { void unreferenced() { std::cout << "Unreferenced"; } void referenced() { std::cout << "Referenced"; } } static void static_unreferenced() { std::cout << "Unreferenced"; } static void static_referenced() { std::cout << "Referenced"; } int main() { referenced(); static_referenced(); return 0; } 

Comstackr ce code avec VS 2017 (en spécifiant l'indicateur de niveau 4 / W4 pour activer l' avertissement C4505: la fonction locale non référencée a été supprimée ) et gcc 4.9 avec l'option -Wunused-function ou -Wall indique que VS 2017 ne produira qu'un avertissement pour la fonction statique non utilisée. gcc 4.9 et les versions ultérieures, ainsi que les clangs 3.3 et supérieurs, produiront des avertissements pour la fonction non référencée dans l’espace de noms et un avertissement pour la fonction statique inutilisée.

Démo en direct de gcc 4.9 et MSVC 2017

Personnellement, je préfère les fonctions statiques aux espaces de noms sans nom pour les raisons suivantes:

  • il est évident et clair de la seule définition de la fonction qu’elle est privée à l’unité de traduction où elle est compilée. Avec un espace de noms sans nom, vous devrez peut-être faire défiler et rechercher pour voir si une fonction est dans un espace de noms.

  • Les fonctions dans les espaces de noms peuvent être traitées comme externes par certains compilateurs (plus anciens). Dans VS2017, ils sont toujours externes. Pour cette raison, même si une fonction est dans un espace de noms sans nom, vous pouvez toujours vouloir les marquer comme étant statiques.

  • les fonctions statiques se comportent de manière très similaire en C ou en C ++, alors que les espaces de noms sans nom ne sont évidemment que C ++. les espaces de noms sans nom ajoutent également un niveau supplémentaire si l’indentation et je n’aime pas ça 🙂

Donc, je suis heureux de voir que l’utilisation de la fonction statique pour les fonctions n’est plus obsolète .