web-dev-qa-db-fra.com

C ++, puis-je initialiser statiquement une carte std :: map au moment de la compilation?

Si je code cela

std::map<int, char> example = {
                                (1, 'a'),
                                (2, 'b'),
                                (3, 'c') 
                              };

alors g ++ me dit

deducing from brace-enclosed initializer list requires #include <initializer_list>
in C++98 ‘example’ must be initialized by constructor, not by ‘{...}’   

et cela me dérange un peu car le constructeur est à l'exécution et peut, théoriquement, échouer.

Bien sûr, si c'est le cas, il échouera rapidement et devrait le faire de manière cohérente, de sorte que je devrais rapidement localiser et corriger le problème.

Mais, encore, je suis curieux - est-il possible d'initialiser une carte, un vecteur, etc., au moment de la compilation?


Edit: j'aurais dû dire que je développe pour les systèmes embarqués. Tous les processeurs n'auront pas de compilateur C++ 0x. Le plus populaire le sera probablement, mais je ne veux pas rencontrer de problème et avoir à maintenir 2 versions du code.

Quant à Boost, je suis indécis. Ils sont peu disposés à utiliser leurs classes Finite State Machine dans les systèmes embarqués, c'est donc en fait ce que je code ici, les classes Event/State/Fsm.

Soupir, je suppose que je ferais mieux de jouer prudemment, mais j'espère que cette discussion a été utile pour les autres.

40
Mawg

Pas en C++ 98. C++ 11 le supporte, donc si vous activez les drapeaux C++ 11 et incluez ce que g ++ suggère, vous pouvez.

Edit: à partir de gcc 5, C++ 11 est activé par défaut

21
Artyom

Ce n'est pas exactement l'initialisation statique, mais essayez-le. Si votre compilateur ne prend pas en charge C++ 0x, je choisirais std :: map's constructeur d'itérations:

std::pair<int, std::string> map_data[] = {
    std::make_pair(1, "a"),
    std::make_pair(2, "b"),
    std::make_pair(3, "c")
};

std::map<int, std::string> my_map(map_data,
    map_data + sizeof map_data / sizeof map_data[0]);

Ceci est assez lisible, ne nécessite aucune bibliothèque supplémentaire et devrait fonctionner dans tous les compilateurs.

37
Dmitry

Vous pouvez utiliser la bibliothèque Boost.Assign :

#include <boost/assign.hpp>
#include <map>
int main()
{
   std::map<int, char> example = 
      boost::assign::map_list_of(1, 'a') (2, 'b') (3, 'c');
}

Cependant, comme Neil et d'autres l'ont souligné dans les commentaires ci-dessous, cette initialisation se produit lors de l'exécution, de manière similaire à la proposition d'UncleBean.

14
mloskot

Avec C++ 0x, vous devrez peut-être utiliser des accolades complètement (utilisez également la syntaxe de nouveau style pour chaque paire):

std::map<int, char> example = { {1,'a'}, {2, 'b'}, {3, 'c'} };

Ces crochets pour construire des paires n'ont pas de sens. Alternativement, vous pouvez nommer complètement chaque paire ou utiliser make_pair (comme vous le feriez en C++ 98)

std::map<int, char> example = {
    std::make_pair(1,'a'),
    std::make_pair(2, 'b'),
    std::make_pair(3, 'c')
};

Quant à la création de ces instances au moment de la compilation: non. Tous les conteneurs STL encapsulent entièrement la gestion de la mémoire d'exécution.

Je suppose que vous n'auriez vraiment qu'une carte au moment de la compilation avec des bibliothèques comme la métaprogrammation de boost (pas sûr à 100%, si elle est entièrement correcte, et vous n'avez pas étudié à quoi cela pourrait être bon):

using namespace boost::mpl;
map<
    pair<integral_c<int, 1>, integral_c<char, 'a'> >,
    pair<integral_c<int, 2>, integral_c<char, 'b'> >,
    pair<integral_c<int, 3>, integral_c<char, 'c'> >
> compile_time_map;
13
UncleBens

Avec le pré-C++ 0x, la chose la plus proche que vous pouvez obtenir est de ne pas utiliser de conteneurs conçus pour une utilisation à l'exécution (et de vous limiter aux types et agrégats fondamentaux) :

struct pair { int first; char second; };
pair arr[] = {{1,'a'}, {2,'b'}}; // assuming arr has static storage

Cela pourrait ensuite être accessible en utilisant une sorte de vue de carte , ou vous pourriez implémenter un wrapper qui permet une initialisation agrégée similaire à ce que Boost. Array le fait.

Bien sûr, la question est de savoir si les avantages justifient le temps consacré à sa mise en œuvre.

Si ma lecture est correcte ici, les listes d'initialisation C++ 0x peuvent vous donner une initialisation statique de non-agrégats comme std::map et std::pair, mais seulement si cela ne change pas la sémantique par rapport à l'initialisation dynamique.
Ainsi, il me semble que vous ne pouvez obtenir ce que vous avez demandé que si votre implémentation peut vérifier via une analyse statique que le comportement ne fonctionne pas. t changer si le map est initialisé statiquement, mais aucune garantie que cela se produit.

4
Georg Fritzsche

Il existe une astuce que vous pouvez utiliser, mais uniquement si ces données ne seront pas utilisées dans un autre constructeur statique. Définissez d'abord une classe simple comme celle-ci:

typedef void (*VoidFunc)();
class Initializer
{
  public:
    Initializer(const VoidFunc& pF)
    {
      pF();
    }
};


Ensuite, utilisez-le comme ceci:

std::map<std::string, int> numbers;
void __initNumsFunc()
{
  numbers["one"] = 1;
  numbers["two"] = 2;
  numbers["three"] = 3;
}
Initializer __initNums(&__initNumsFunc);


Bien sûr, c'est un peu exagéré, donc je recommanderais de l'utiliser seulement si vous en avez vraiment besoin.

1
Adis H

Il n'existe aucun moyen standard d'initialiser std::map au moment de la compilation. Comme d'autres l'ont mentionné, C++ 0x permettra au compilateur d'optimiser l'initialisation pour être statique si possible, mais cela ne sera jamais garanti.

N'oubliez pas, cependant, que la STL n'est qu'une spécification d'interface. Vous pouvez créer vos propres conteneurs conformes et leur donner une capacité d'initialisation statique.

Selon que vous prévoyez de mettre à niveau votre compilateur et votre implémentation STL (en particulier sur une plate-forme intégrée), vous pouvez même simplement creuser dans l'implémentation que vous utilisez, ajouter des classes dérivées et les utiliser!

1
Potatoswatter