2010-12-09 112 views
12

J'utilise une carte comme un tableau associatif d'ID -> valeur, où la valeur est une struct définition de l'objet:Comment initialiser une carte qui prend une structure comme valeur?

#include <map> 

struct category { 
     int id; 
     std::string name; 
}; 

std::map<int, category> categories; 

int main() { 
     categories[1] = {1, "First category"}; 
     categories[2] = {2, "Second category"}; 

} 

Le code ci-dessus compile avec g ++, mais avec l'avertissement suivant:

warning: extended initializer lists only available with -std=c++0x or -std=gnu++0x 

J'ai lu diverses questions/réponses ici sur l'initialisation de la structure, mais je suis encore un peu confus. J'ai une série de questions connexes:

  1. Je pourrais ajouter l'option du compilateur std = C++ 0x et être fait avec l'avertissement, mais toujours pas les plus sages sur le problème sous-jacent. Les choses ne se casseraient-elles pas si j'ajoutais une méthode à la catégorie struct? Quel serait le meilleur moyen d'initialiser cette structure POD (catégorie) d'une manière plus conforme à C++ 03? Fondamentalement, je ne suis pas encore sûr des conséquences de faire les choses d'une manière plutôt que d'une autre manière. Ce type de tableau associatif (où la clé est l'ID d'un objet) est facile avec PHP, et j'apprends toujours la bonne façon de le faire en C++. Y a-t-il quelque chose que je devrais faire attention dans le contexte du code ci-dessus?

Modifier
Les questions suivantes sont liées, mais je ne comprenais pas les réponses quand je les lis:
C++ initialize anonymous struct
c++ Initializing a struct with an array as a member
Initializing structs in C++

Répondre

17

En C++ (ISO/IEC 14882: 2003), une liste d'expressions jointes peut être utilisée pour initialiser une variable de type aggregate dans la déclaration qui la définit.

E.g.

struct S { int a; std::string b; }; 

S x = { 39, "Hello, World\n" }; 

Un agrégat de typeest un tableau ou d'une classe avec aucun constructeur déclarée par l'utilisateur, aucun membre de données non statiques privés ou protégés, pas de classes de base, et pas de fonctions virtuelles. Notez qu'une classe agrégée ne doit pas nécessairement être une classe POD et tout tableau est un agrégé si le type dont il s'agit est un agrégat ou non. Cependant, une liste d'expressions entre accolades n'est valide qu'en tant qu'initialisateur pour un agrégat, elle n'est généralement pas autorisée dans d'autres contextes tels que l'affectation ou la liste d'initialisation d'un constructeur de classe.

Dans le projet actuel de la prochaine version de C++ (C++ 0x), une liste fermée accolade d'expressions (accolades liste d'initialisation) est autorisée dans plusieurs contextes et lorsqu'un objet est initialisé à partir d'un tel liste d'initialisation il est appelé initialisation de la liste.

De nouveaux contextes où une telle liste est autorisée incluent des arguments dans un appel de fonction, des fonctions retournées, des arguments aux constructeurs, des initialiseurs de membre et de base et du côté droit d'une affectation.

Cela signifie que ceci n'est pas valide en C++ 03.

int main() { 
     categories[1] = {1, "First category"}; 
     categories[2] = {2, "Second category"}; 
} 

Au lieu de cela, vous pouvez faire quelque chose comme ça.

int main() { 
     category tmp1 = { 1, "First category" }; 
     category tmp2 = { 2, "Second category" }; 

     categories[1] = tmp1; 
     categories[2] = tmp2; 
} 

En variante.

int main() { 
     category tmpinit[] = { { 1, "First category" }, 
           { 2, "Second category" } }; 
     categories[1] = tmpinit[0]; 
     categories[2] = tmpinit[1]; 
} 

Ou, vous pourriez envisager de faire une fonction d'usine pour votre type. (Vous pourriez ajouter un constructeur pour votre type mais cela rendrait votre classe non agrégée et vous empêcherait d'utiliser l'initialisation agrégée dans d'autres endroits.)

category MakeCategory(int n, const char* s) 
{ 
    category c = { n, s }; 
    return c; 
} 

int main() 
{ 
    categories[1] = MakeCategory(1, "First category"); 
    categories[2] = MakeCategory(2, "Second category"); 
} 
+0

Enfin quelqu'un qui a tout compris. Merci, Charles! – sbi

+0

Merci à Charles Bailey d'avoir pris le temps de résumer toute la discussion. Merci @sbi pour avoir poussé ça. J'apprécie beaucoup tout. :) – augustin

+0

BTW, le titre de la question ne reflète finalement pas le contenu de la discussion.N'hésitez pas à modifier le titre de la question pour quelque chose qui serait plus approprié. – augustin

10

Dans la norme actuelle C++ , vous pouvez utiliser des listes d'initialisation pour initialiser des tableaux et des structures contenant uniquement des valeurs POD. Le prochain standard (aka C++ 0x ou C++ 1x) permettra de faire la même chose sur des structures contenant des types non-POD, par ex. std :: string. C'est l'objet de l'avertissement.

Je vous suggère d'ajouter un constructeur simple à category qui prend l'identification et le nom et simplement appeler ce constructeur à la place:

#include <map> 
#include <string> 

struct category { 
     category() : id(0), name() {} 
     category(int newId, std::string newName) 
     : id(newId), name(newName) {} 

     int id; 
     std::string name; 
}; 

std::map<int, category> categories; 

int main() { 
     categories[1] = category(1, "First category"); 
     categories[2] = category(2, "Second category"); 

} 
+0

+1, merci. Cela fonctionne de cette façon. Bizarrement, le code ne compile pas si j'omets la ligne avec le constructeur vide category :: category().Apparemment, Map en a besoin à des fins de comparaison: /usr/include/c++/4.4/bits/stl_map.h:450: erreur: aucune fonction correspondante pour l'appel à 'category :: category()' – augustin

+3

Ce n'est pas étrange, mais un comportement standard . Tout type que vous voulez stocker dans un conteneur STL a besoin d'un constructeur par défaut. Et les classes et les structures sans constructeur en obtiennent une automatiquement, mais dès que vous définissez la vôtre, vous devrez également en définir une par défaut. – Mephane

+0

Oh, je vois. Merci pour l'explication claire. :) – augustin

3

le genre d'initialisation que nous utilisons est introduit que dans les pays émergents standard C++ appelé C++ 0x, d'où l'avertissement et l'option du compilateur. Certains compilateurs, comme g ++, supportent déjà certaines des nouvelles fonctionnalités, mais la norme elle-même n'est pas encore acceptée. Il ajoute de nombreuses nouvelles fonctionnalités à C++ tel que nous le connaissons. Vous pouvez lire plus sur Stroustrup's site.

pour initialiser la structure, vous pouvez ajouter un ctor (naturellement), par ex.

struct category { 
     category(int i, const std::string& n): id(i), name(n) {} 
     int id; 
     std::string name; 
}; 

puis pour initialiser la carte comme suit:

categories[1] = category(1, "First category"); 

Notez qu'une conversion implicite de const char* à la chaîne fonctionnera ici, ou bien vous pouvez définir un cteur avec const char* aussi.

+0

et Mephane a posté à peu près la même réponse à 1 minute d'intervalle. La réponse est la même, mais pour le constructeur vide, et le code ne compilera pas sans lui. Voir mon commentaire dans la réponse de Mephane. En tout cas, cela confirme que l'utilisation d'un constructeur est la voie à suivre. Merci. (et +1) – augustin

+0

@augustin: correct re vide ctor. Une note, je préférerais le paramètre const ref en passant 'const std :: string & n' sur' std :: string n', pour éviter l'allocation de mémoire temporaire et la copie. Et merci pour la reconnaissance :) – davka

+0

Bon point sur const std :: string & n. Et merci pour votre aide. – augustin

1

la fonctionnalité dont vous avez besoin est appelée agrégée en C/C++.En effectuant une recherche sur "agrégat C++", vous trouverez de nombreuses informations détaillant le pourquoi et le comment.

1- Wouldn't things break if I add a method to the category struct?

N'est pas nécessaire sauf si la méthode influence la disposition de la mémoire C++ sous-jacente. Par exemple, une fonction ordinaire n'a pas d'importance, mais une fonction virtuelle le fera parce qu'elle est probablement présentée aux membres de la classe.

2- What would the best way be to initialize this POD struct (category) in a more c99 compliant way?

en utilisant des constructeurs comme le suggèrent les autres répondants.

3- Is there anything I should pay attention to in the context of the code above?

Il peut impliquer des copies redondantes selon la façon dont vous concevez vous constructeur. mais cela n'a d'importance que si vous avez souvent besoin de l'initialisation et que vous vous souciez vraiment de la performance.

+0

+1, merci. C'est quelque chose de nouveau pour moi, et je suis en train de l'examiner maintenant comme vous le suggérez. – augustin

+0

Grâce à votre suggestion, j'ai trouvé cette page utile. Initialisation de l'agrégat: http://www.codeguru.com/cpp/tic/tic0077.shtml – augustin

+0

Et aussi: http://en.wikipedia.org/wiki/C++classes – augustin