Question Namespaces sans nom / anonymes vs fonctions statiques


Une fonctionnalité de C ++ est la possibilité de créer des espaces de noms anonymes (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 noms, il est impossible d'y accéder depuis l'extérieur. Mais ces espaces de noms sans nom sont accessible dans le fichier dans lequel ils sont créés, comme si vous aviez une clause d'utilisation implicite pour eux.

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


418
2017-09-30 19:02


origine


Réponses:


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é lors de la déclaration d'objets dans un   portée de l'espace de noms, l'espace de noms sans nom   fournit une alternative supérieure.   


285
2017-09-30 19:06



Mettre des méthodes dans un espace de noms anonyme vous empêche de violer accidentellement la Une règle de définition, vous permettant de ne jamais vous soucier de nommer vos méthodes d'assistance de la même manière qu'une autre méthode que vous pouvez lier.

Et, comme indiqué par luke, les espaces de noms anonymes sont préférés par les standards sur les membres statiques.


57
2017-09-30 23:20



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 identifiant 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 pas foo(S const &) comme on peut s'y attendre.

template <typename T>
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 ++ entièrement conforme (c.-à-d. Avec un support pour export) la static mot-clé aura toujours des fonctionnalités qui ne sont pas disponibles d'une autre manière.

// bar.h
export template <typename T>
int b1 (T const & t);

// bar.cc
#include "bar.h"
template <typename T>
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 nom 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 possède 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 consultation 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 les espaces de noms associés (3.4.2), seules les déclarations de fonction trouvées dans le contexte de définition de modèle ou le contexte d'instanciation de modèle sont trouvées.

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


31
2017-10-01 09:15



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 de noms n'étaient plus disponibles pour l'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 nom XmlUtil_I {...} pour toutes mes variables et fonctions de 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 désavantage potentiel de cette approche par rapport à une approche vraiment anonyme est que quelqu'un pourrait violer la portée statique désirée en utilisant le qualificatif d'espace de noms dans d'autres modules. Je ne sais pas si c'est une préoccupation majeure cependant.


10
2017-10-01 00:39



L'utilisation d'un mot-clé statique à cette fin est déconseillée par la norme C ++ 98. Le problème avec 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, de sorte que les espaces de noms non nommés simplifient un peu les choses.


7
2017-09-30 19:13



D'expérience, je vais juste noter que, bien que ce soit le moyen C ++ de placer des fonctions anciennement 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 sommes 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 que ça pourrait faire quand il accepte il. Donc, tant que nous ne sommes pas modernes, nous utilisons toujours des fonctions statiques (généralement classées) où nous préférons l’espace de noms anonyme.


6
2017-10-26 20:43



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


3
2017-12-08 19:02



Ayant appris cette fonctionnalité juste maintenant 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 imbriqués 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.


2
2017-09-30 19:09



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 <iostream>

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;
}

Compilation de ce code avec VS 2017 (en spécifiant l'indicateur d'avertissement de niveau 4 / W4 pour activer 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 indiquent que VS 2017 ne produira qu'un avertissement pour la fonction statique inutilisé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


1
2018-04-18 06:16