web-dev-qa-db-fra.com

Comment initialiser une carte qui prend une structure comme valeur?

J'utilise une carte comme un tableau associatif d'ID -> valeur, où la valeur est une structure définissant l'objet:

#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 se 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 toujours un peu confus. J'ai une série de questions connexes:

  1. Je pourrais ajouter l'option du compilateur -std = c ++ 0x et en finir avec l'avertissement, mais je ne serai toujours pas plus sage au sujet du problème sous-jacent. Les choses ne se briseraient-elles pas si j'ajoutais une méthode à la structure de catégorie?

  2. Quelle serait la meilleure façon d'initialiser cette structure (catégorie) POD d'une manière plus conforme à C++ 03?

  3. En gros, je ne suis pas encore sûr des conséquences de faire les choses d'une manière plutôt que d'une autre. 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 n'ai pas compris les réponses lorsque je les ai lues pour la première fois:
C++ initialise la structure anonyme
c ++ Initialisation d'une structure avec un tableau comme membre
Initialisation des structures en C++

17
augustin

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

Par exemple.

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

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

Un type global est un tableau ou une classe sans constructeurs déclarés par l'utilisateur, sans membres de données non statiques privés ou protégés, sans classes de base et sans fonctions virtuelles. Notez qu'une classe agrégat ne doit pas être une classe POD et tout tableau est un agrégat que le type dont il s'agit soit un agrégat.

Cependant, une liste d'expressions entre accolades n'est valide que comme initialiseur 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 des membres d'un constructeur de classe.

Dans le brouillon actuel de la prochaine version de C++ (C++ 0x), une liste d'expressions entre accolades (brace-init-list) est autorisée dans plus de contextes et lorsqu'un objet est initialisé à partir de telles an liste d'initialisation il s'appelle initialisation de liste.

Les nouveaux contextes où une telle liste est autorisée incluent les arguments dans un appel de fonction, les retours de fonction, les arguments aux constructeurs, les initialiseurs de membre et de base et sur le côté droit d'une affectation.

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

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

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

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

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

Alternativement.

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

Vous pouvez également envisager de créer une fonction d'usine pour votre type. (Vous pouvez ajouter un constructeur pour votre type mais cela ferait de votre classe un non-agrégat et vous empêcherait d'utiliser l'initialisation d'agrégat à 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" );
}
20
CB Bailey

Dans la norme C++ actuelle, 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 de même sur les structures contenant des types non-POD, par ex. std :: string. C'est à cela que sert l'avertissement.

Je vous suggère d'ajouter un constructeur simple à category qui prend l'identifiant et le nom et d'appeler simplement 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");

}
10
Mephane

Je sais que c'est vieux, mais on peut aussi utiliser

std::map<int, std::pair<std::string, int>> categories

ou:

std::map<int, std::Tuple<std::string, int, double>> categories

si on a besoin de plus.

3
AKJ

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

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 d'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 ctor avec const char* aussi.

3
davka

la fonctionnalité dont vous avez besoin est appelée agrégat en C/C++. En recherchant "c ++ agrégé", vous trouverez beaucoup d'informations détaillant les pourquoi et les comment.

1- Les choses ne se briseraient-elles pas si j'ajoutais une méthode à la structure de catégorie?

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

2- Quelle serait la meilleure façon d'initialiser cette structure (catégorie) POD d'une manière plus conforme à c99?

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

3- Y a-t-il quelque chose à quoi je dois faire attention dans le contexte du code ci-dessus?

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

1
t.g.