2
votes

Initialisation de la carte à partir de données statiques membres d'autres classes

Nous avons des classes qui ont exposé le membre de données statique en tant que membres publics comme

static const map<string, string> versionMap = {{A().NAME, "Aa"},
                                               {B().NAME, "Bb"}
                                              };

Et de même la classe B et quelques autres classes.

Si je dois créer une carte quelque chose comme

class A{
public:
    static const string NAME;
    //Other class specific methods
};

Objets temporaires de classes créés lors de la création de versionMap, est-il garanti d'avoir un comportement défini à tout moment?


2 commentaires

Puisqu'il est statique, vous n'avez pas besoin d'un objet pour y accéder. Utilisez simplement A::NAME , B::NAME , etc. Mais cela ne résout pas les problèmes d'ordre d'initialisation des variables statiques.


J'éviterais d'utiliser des structures de données allouées dynamiquement en tant que membres statiques, dans la mesure du possible. Rappelez-vous qu'ils ne peuvent pas être utilisés dans le travail constexpr ... pourquoi ne pas en faire un std::string_view ?


3 Réponses :


0
votes

Lorsque vous définissez un membre statique d'une classe, que cette classe soit ou non instanciée, ce membre est accessible via la mémoire. Dans ce cas, puisqu'ils sont des membres publics, l'utilisation des chaînes elles-mêmes comme clés est parfaitement valide.

Cela dit, vos membres constants statiques doivent être initialisés après la définition de classe, généralement quelque chose comme:

// Compiled with g++ --std=c++17 -Wall -Wextra -Werror ConstStaticMapExample.cpp -o ConstStaticMapExample
#include <iostream>
#include <string>
#include <map>

class A
{
public:
  static const std::string NAME;
  //Other class specific methods
};
const std::string A::NAME = "foo";

class B
{
public:
  static const std::string NAME;
  //Other class specific methods
};
const std::string B::NAME = "bar";

class C
{
public:
  static const std::map<std::string, std::string> versionMap;
  // More definitions
};
const std::map<std::string, std::string> C::versionMap = {{A::NAME,"aA"},{B::NAME,"bB"}}; // Reversed for explanation

int main(int,char**)
{
  // local static
  static const std::map<std::string, std::string> versionMap = {{A::NAME,"Aa"},{B::NAME,"Bb"}};
  std::cout << "LOCAL STATIC EXAMPLE:" << std::endl;
  for(auto mpair : versionMap)
  {
    std::cout << "Key: " << mpair.first << "\tVal: " << mpair.second << std::endl;
  }

  // class member static
  std::cout << "CLASS MEMBER STATIC EXAMPLE:" << std::endl;
  for(auto mpair : C::versionMap)
  {
    std::cout << "Key: " << mpair.first << "\tVal: " << mpair.second << std::endl;
  }

  return 0;
}

Pour votre exemple de carte statique, vous devez garder à l'esprit que la carte doit être initialisée de la même manière si elle est utilisée en tant que membre de classe. Voici un exemple de travail:

class Foo { static const int foo; };
const int Foo::foo = 42;

Essentiel


0 commentaires

1
votes

Il semble que vous implémentiez une sorte de mécanisme de réflexion, avec des noms de classe. Pourquoi ne pas obtenir les noms de classes, en tant que noms?

Jettes un coup d'oeil à:

Puis-je obtenir des noms de type C ++ de manière constexpr?

la réponse acceptée ici vous permet d'écrire get_name<A>() et d'obtenir une chaîne (_view) qui est "A". Et get_name<B>() sera "B" et ainsi de suite. Cela peut être utile dans votre cas, car:

  1. Pas de données allouées dynamiquement.
  2. Aucune construction d'objets (bien que @ jhill515 vous le donne également)
  3. Pas besoin de membres statiques - cela peut fonctionner avec des classes que vous ne contrôlez même pas!

0 commentaires

1
votes

Il est garanti de ne fonctionner que si

  • la carte est définie dans la MÊME UNITÉ DE COMPILATION où le membre statique est défini (par exemple A.cpp )
  • la carte est définie APRÈS la définition du membre de données statique

Vous jouez cependant avec le feu ... au lieu de dépendre de l'ordre d'initialisation statique, il est probablement préférable d'utiliser une variable statique locale définie à l'intérieur d'une fonction membre statique ; c'est à dire

// File A.h

struct A {
    static const std::string& NAME();
};

// File Map.cpp

std::map<std::string, std::string> x{{A::NAME(), "A"}};

// File A.cpp

static const std::string& A::NAME() {
    static std::string x = "A string";
    return x;
}

Cette approche est garantie de fonctionner car les variables statiques locales sont initialisées à la première utilisation (et même automatiquement protégées pour les problèmes de multithreading en C ++ 11).


0 commentaires