web-dev-qa-db-fra.com

Espaces de noms non nommés / anonymes et fonctions statiques

Une fonctionnalité de C++ est la possibilité de créer des espaces de noms non nommés (anonymes), comme suit:

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'accéder à quoi que ce soit en son sein depuis l'extérieur. Mais ces espaces de noms non nommés sont sont accessibles dans le fichier dans lequel ils ont été créés, comme si vous aviez une clause using implicite.

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

476
Head Geek

La norme C++, dans la section 7.3.1.1 Espaces de nom non nommés, est décrite au paragraphe 2:

L'utilisation du mot clé static est déconseillée lors de la déclaration d'objets dans une portée d'espace de noms. L'espace de noms non-nommé fournit une alternative supérieure.

Static ne s'applique qu'aux noms d'objets, de fonctions et d'unions anonymes, pas aux déclarations de type.

Modifier:

La décision de déconseiller cette utilisation du mot clé static (affectant la visibilité d'une déclaration de variable dans une unité de traduction) a été annulée ( ref ). Dans ce cas, utiliser un espace de noms statique ou non nommé revient à être essentiellement deux façons de faire exactement la même chose. Pour plus de discussion, veuillez vous reporter à this SO question.

Les espaces de noms non nommés ont toujours l'avantage de vous permettre de définir des types locaux d'unités de traduction. S'il vous plaît voir this SO question pour plus de détails.

Le mérite revient à Mike Percy pour avoir porté cela à mon attention.

312
luke

Le fait de placer des méthodes dans un espace de noms anonyme vous évite de violer accidentellement règle de définition unique , ce qui vous permet de ne jamais vous inquiéter de nommer vos méthodes auxiliaires de la même façon qu’une autre méthode à laquelle vous pouvez créer un lien.

Et, comme le souligne luke, les espaces de noms anonymes sont préférés par la norme par rapport aux membres statiques.

66
hazzen

Il y a un cas Edge où statique a un effet surprenant (du moins c'était pour moi). La norme C++ 03 stipule dans 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 non qualifié-id mais pas un template-id , les fonctions candidates sont trouvées à l'aide des règles de recherche habituelles (3.4.1, 3.4.2), à ceci près 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 une liaison externe à partir du contexte de définition de modèle sont trouvées.
  • Pour la partie de la recherche utilisant les espaces de nom associés (3.4.2), seules les déclarations de fonction avec liaison externe trouvées dans le contexte de définition de modèle ou dans le contexte d'instanciation de modèle sont trouvées.

...

Le code ci-dessous appellera foo(void*) et non pas foo(S const &) comme on pourrait 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 un gros problème, mais il faut souligner que pour un compilateur C++ entièrement compatible (c'est-à-dire qui prend en charge export), le mot-clé static aura toujours une fonctionnalité qui n'est pas disponible dans de toute 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);
}

Le seul moyen de vous assurer que la fonction de notre espace de noms non nommé ne se retrouvera pas dans les modèles utilisant ADL consiste à la rendre static.

Mise à jour pour Modern C++

À partir de C++ '11, les membres d'un espace de nom non nommé ont un lien interne implicite (3.5/4):

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

Mais dans le même temps, 14.6.4.2/1 a été mis à jour pour supprimer la mention de liaison (ceci provient de C++ '14):

Pour un appel de fonction où l'expression postfixe est un nom dépendant, les fonctions candidates sont trouvées à l'aide des règles de recherche habituelles (3.4.1, 3.4.2), à ceci près 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 nom associés (3.4.2), seules les déclarations de fonction trouvées dans le contexte de définition de modèle ou dans le contexte d'instanciation de modèle sont trouvées.

Il en résulte que cette différence particulière entre les membres d'espace de nom statique et non nommé n'existe plus.

34
Richard Corden

J'ai récemment commencé à remplacer des mots clés statiques par des espaces de noms anonymes dans mon code, mais j'ai immédiatement rencontré un problème qui empêchait l'inspection des variables de l'espace de noms dans mon débogueur. J'utilisais VC60, donc je ne sais pas si cela ne pose aucun problème avec les autres débogueurs. Ma solution de contournement consistait à définir un espace de noms 'module', auquel je lui donnais 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 :: du débogueur pour accéder aux variables. Dans ce cas, le '_I' le distingue d'un espace de noms public tel que XmlUtil que je pourrais vouloir utiliser ailleurs.

Je suppose qu'un inconvénient potentiel de cette approche par rapport à une approche réellement anonyme est que quelqu'un pourrait violer l'étendue statique souhaité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.

11
William Knight

L'utilisation d'un mot clé statique à cette fin est déconseillée par le standard C++ 98. Le problème avec static est que cela ne s'applique pas à la définition de type. Il s'agit également d'un mot clé surchargé utilisé de différentes manières dans différents contextes. Les espaces de nom non nommés simplifient donc un peu les choses.

7
Firas Assaad

Par expérience, je noterai simplement que s'il s'agit de la méthode C++ pour insérer des fonctions auparavant 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 convient parfaitement pour placer des fonctions dans l'espace de noms anonyme.

Mais un ancien compilateur fonctionnant sous Solaris, auquel nous sommes rattachés jusqu’à une future version indéterminée, l’acceptera parfois et le signalera parfois comme une erreur. L'erreur n'est pas ce qui m'inquiète, c'est ce qu'elle peut-être faire quand elle accepte la. Donc, jusqu'à ce que nous soyons modernes dans tous les domaines, nous utilisons toujours des fonctions statiques (généralement classées par classe) dans lesquelles nous préférerions l'espace de noms anonyme.

6
Don Wakefield

De plus si on utilise un mot clé statique sur une variable comme celle-ci:

namespace {
   static int flag;
}

Il ne serait pas vu dans le fichier de mappage

4
Chris

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

  • Les espaces de noms anonymes peuvent être imbriqués les uns dans les autres, offrant ainsi plusieurs niveaux de protection auxquels les symboles ne peuvent pas échapper.
  • Plusieurs espaces de noms anonymes peuvent être placés dans le même fichier source, ce qui crée différentes portées de niveau statique dans le même fichier.

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

2
Commodore Jaeger

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

Compiler ce code avec VS 2017 (en spécifiant l'indicateur d'avertissement de niveau 4/W4 à 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 un avertissement que pour la fonction statique non utilisée. gcc 4.9 et supérieur, ainsi que clang 3.3 et supérieur, produiront des avertissements pour la fonction non référencée dans l'espace de noms, ainsi qu'un avertissement pour la fonction statique non utilisée.

Démo en direct de gcc 4.9 et MSVC 2017

2
masrtis

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

  • la définition de fonction seule indique clairement que c'est une tâche privée de 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 de manière statique.

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

Donc, je suis heureux de voir que l'utilisation de static pour les fonctions n'est plus obsolète .

0
Pavel