web-dev-qa-db-fra.com

Initialisation de la structure C++

Est-il possible d'initialiser des structures en C++ comme indiqué ci-dessous

struct address {
    int street_no;
    char *street_name;
    char *city;
    char *prov;
    char *postal_code;
};
address temp_address =
    { .city = "Hamilton", .prov = "Ontario" };

Les liens ici et ici indiquent qu'il est possible d'utiliser ce style uniquement en C. Si tel est le cas, pourquoi n'est-ce pas possible en C++? Existe-t-il une raison technique sous-jacente pour laquelle il n'est pas implémenté en C++ ou est-il déconseillé d'utiliser ce style? J'aime utiliser cette méthode d'initialisation car ma structure est grande et que ce style me donne une lisibilité claire de la valeur attribuée à quel membre.

S'il vous plaît partager avec moi s'il existe d'autres moyens par lesquels nous pouvons atteindre la même lisibilité.

J'ai référé les liens suivants avant de poster cette question

  1. C/C++ pour AIX
  2. Initialisation de la structure en C avec une variable
  3. Initialisation de structure statique avec des balises en C++
  4. Initialisation de structure propre C++ 11
225
Dinesh P.R.

Si vous voulez préciser la valeur de chaque initialiseur, divisez-la en plusieurs lignes, avec un commentaire sur chacune d'elles:

address temp_addres = {
  0,  // street_no
  nullptr,  // street_name
  "Hamilton",  // city
  "Ontario",  // prov
  nullptr,  // postal_code
};
144
Wyzard

Après ma question n'a abouti à aucun résultat satisfaisant (car C++ n'implémente pas les init basées sur les balises), j'ai pris le tour trouvé ici: Les membres d'une structure C++ sont-ils initialisés à 0 par défaut?

Pour vous, cela reviendrait à faire cela:

address temp_address = {}; // will zero all fields in C++
temp_address.city = "Hamilton";
temp_address.prov = "Ontario";

C’est certainement le plus proche de ce que vous vouliez à l’origine (mettez à zéro tous les champs sauf ceux que vous souhaitez initialiser).

87
Gui13

Les identifiants de champ sont bien la syntaxe d'initialiseur C. En C++, donnez simplement les valeurs dans le bon ordre sans les noms de champs. Malheureusement, cela signifie que vous devez tous les donner (en fait, vous pouvez omettre les champs de valeur zéro et le résultat sera identique):

address temp_address = { 0, 0, "Hamilton", "Ontario", 0 }; 
13
Gene

Cette fonctionnalité est appelée initializer désignés . C'est un ajout à la norme C99. Cependant, cette fonctionnalité n'a pas été incluse dans C++ 11. Selon le langage de programmation C++, 4ème édition, Section 44.3.3.2 (Fonctionnalités C non adoptées par C++):

Quelques ajouts à C99 (comparés à C89) ont été délibérément non adoptés en C++: 

[1] Tableaux de longueur variable (VLA); utiliser un vecteur ou une forme de tableau dynamique 

[2] Initialisateurs désignés; utiliser des constructeurs

La grammaire C99 a les initialiseurs désignés [Voir ISO/CEI 9899: 2011, Projet de comité N1570 - 12 avril 2011]

6.7.9 Initialisation

initializer:
    assignment-expression
    { initializer-list }
    { initializer-list , }
initializer-list:
    designation_opt initializer
    initializer-list , designationopt initializer
designation:
    designator-list =
designator-list:
    designator
    designator-list designator
designator:
    [ constant-expression ]
    . identifier

Par ailleurs, le C++ 11 ne possède pas les initialiseurs désignés [Voir ISO/CEI 14882: 2011, Projet de comité N3690 - 15 mai 2013]

8.5 Initialisations

initializer:
    brace-or-equal-initializer
    ( expression-list )
brace-or-equal-initializer:
    = initializer-clause
    braced-init-list
initializer-clause:
    assignment-expression
    braced-init-list
initializer-list:
    initializer-clause ...opt
    initializer-list , initializer-clause ...opt
braced-init-list:
    { initializer-list ,opt }
    { }

Pour obtenir le même effet, utilisez des constructeurs ou des listes d’initialisation:

10

Comme d'autres l'ont mentionné, il s'agit d'un initialiseur.

Cette fonctionnalité fait partie de C++ 20

7
sameer chaudhari

Vous pouvez simplement initialiser via ctor:

struct address {
  address() : city("Hamilton"), prov("Ontario") {}
  int street_no;
  char *street_name;
  char *city;
  char *prov;
  char *postal_code;
};
7
nikc

Vous pouvez même intégrer la solution de Gui13 dans une seule instruction d'initialisation:

struct address {
                 int street_no;
                 char *street_name;
                 char *city;
                 char *prov;
                 char *postal_code;
               };


address ta = (ta = address(), ta.city = "Hamilton", ta.prov = "Ontario", ta);

Disclaimer: Je ne recommande pas ce style

7
user396672

Je sais que cette question est assez ancienne, mais j’ai trouvé une autre façon d’initialiser, en utilisant constexpr et currying:

struct mp_struct_t {
    public:
        constexpr mp_struct_t(int member1) : mp_struct_t(member1, 0, 0) {}
        constexpr mp_struct_t(int member1, int member2, int member3) : member1(member1), member2(member2), member3(member3) {}
        constexpr mp_struct_t another_member(int member) { return {member1, member,     member2}; }
        constexpr mp_struct_t yet_another_one(int member) { return {member1, member2, member}; }

    int member1, member2, member3;
};

static mp_struct_t a_struct = mp_struct_t{1}
                           .another_member(2)
                           .yet_another_one(3);

Cette méthode fonctionne également pour les variables statiques globales et même celles constexpr .. Le seul inconvénient est la mauvaise maintenabilité: chaque fois qu'un autre membre doit être initialisé à l'aide de cette méthode, toutes les méthodes d'initialisation des membres doivent être modifiées.

6
Fabian

Ce n'est pas implémenté en C++. (aussi, char* chaînes? J'espère que non).

Habituellement, si vous avez autant de paramètres, c'est une odeur de code assez grave. Mais au lieu de cela, pourquoi ne pas simplement initialiser la structure par la valeur, puis affecter chaque membre?

5
Puppy

Il se peut que je manque quelque chose ici, par pourquoi pas:

#include <cstdio>    
struct Group {
    int x;
    int y;
    const char* s;
};

int main() 
{  
  Group group {
    .x = 1, 
    .y = 2, 
    .s = "Hello it works"
  };
  printf("%d, %d, %s", group.x, group.y, group.s);
}
3
run_the_race

J'ai trouvé cette façon de le faire pour les variables globales, cela ne nécessite pas de modifier la définition de la structure d'origine:

struct address {
             int street_no;
             char *street_name;
             char *city;
             char *prov;
             char *postal_code;
           };

puis déclarez la variable d'un nouveau type hérité du type struct d'origine et utilisez le constructeur pour l'initialisation des champs:

struct temp_address : address { temp_address() { 
    city = "Hamilton"; 
    prov = "Ontario"; 
} } temp_address;

Pas aussi élégant que le style C mais ...

Pour la variable locale devrait également être possible, mais il faut vérifier si un memset supplémentaire (this, 0, sizeof (* this)) serait nécessaire alors ...

(Notez que 'temp_address' est une variable de type 'temp_address', cependant ce nouveau type hérite de 'address' et peut être utilisé partout où on attend 'address', donc c'est OK.)

2
VincentP

C'est possible, mais seulement si la structure que vous initialisez est une structure POD (Plain Old Data). Il ne peut contenir aucune méthode, constructeur ou même valeur par défaut.

1
whoKnows

En C++, les initialiseurs de style C ont été remplacés par des constructeurs qui, au moment de la compilation, peuvent garantir que seules les initialisations valides sont effectuées (c'est-à-dire qu'après l'initialisation, les membres de l'objet sont cohérents).

C'est une bonne pratique, mais parfois une pré-initialisation est pratique, comme dans votre exemple. OOP résout ce problème par des classes abstraites ou des modèles de conception .

À mon avis, l’utilisation de cette méthode sécurisée tue la simplicité et parfois le compromis sécurité s’avère trop coûteux, car un code simple n’a pas besoin d’une conception sophistiquée pour rester maintenable.

Comme solution alternative, je suggère de définir les macros à l'aide de lambdas afin de simplifier l'initialisation pour ressembler presque au style C:

struct address {
  int street_no;
  const char *street_name;
  const char *city;
  const char *prov;
  const char *postal_code;
};
#define ADDRESS_OPEN [] { address _={};
#define ADDRESS_CLOSE ; return _; }()
#define ADDRESS(x) ADDRESS_OPEN x ADDRESS_CLOSE

La macro ADDRESS se développe pour

[] { address _={}; /* definition... */ ; return _; }()

qui crée et appelle le lambda. Les paramètres de macro sont également séparés par des virgules, vous devez donc mettre l’initialiseur entre crochets et appeler comme

address temp_address = ADDRESS(( _.city = "Hamilton", _.prov = "Ontario" ));

Vous pouvez également écrire un initialiseur de macro généralisé

#define INIT_OPEN(type) [] { type _={};
#define INIT_CLOSE ; return _; }()
#define INIT(type,x) INIT_OPEN(type) x INIT_CLOSE

mais l'appel est un peu moins beau

address temp_address = INIT(address,( _.city = "Hamilton", _.prov = "Ontario" ));

toutefois, vous pouvez définir la macro ADDRESS à l’aide de la macro générale INIT facilement.

#define ADDRESS(x) INIT(address,x)
1
Jan Turoň

Dans GNUC++ (semble être obsolète depuis la version 2.5, il y a très longtemps :) Voir les réponses ici: Initialisation de la structure en utilisant des étiquettes. Cela fonctionne, mais comment? ), il est possible d’initialiser une structure comme celle-ci:

struct inventory_item {
    int bananas;
    int apples;
    int pineapples;
};

inventory_item first_item = {
    bananas: 2,
    apples: 49,
    pineapples: 4
};
0
The Science Boy

J'ai rencontré un problème similaire aujourd'hui, où j'ai une structure que je veux remplir avec des données de test qui seront transmises en tant qu'arguments à une fonction que je teste. Je voulais avoir un vecteur de ces structures et je recherchais une méthode à une ligne pour initialiser chaque structure.

Je me suis retrouvé avec une fonction constructeur dans la structure, ce qui, je crois, a également été suggéré dans quelques réponses à votre question.

C'est probablement une mauvaise pratique que les arguments du constructeur portent les mêmes noms que les variables de membre public, ce qui nécessite l'utilisation du pointeur this. Quelqu'un peut suggérer une modification s'il existe un meilleur moyen.

typedef struct testdatum_s {
    public:
    std::string argument1;
    std::string argument2;
    std::string argument3;
    std::string argument4;
    int count;

    testdatum_s (
        std::string argument1,
        std::string argument2,
        std::string argument3,
        std::string argument4,
        int count)
    {
        this->rotation = argument1;
        this->tstamp = argument2;
        this->auth = argument3;
        this->answer = argument4;
        this->count = count;
    }

} testdatum;

Ce que j’ai utilisé dans ma fonction de test pour appeler la fonction à tester avec divers arguments tels que:

std::vector<testdatum> testdata;

testdata.Push_back(testdatum("val11", "val12", "val13", "val14", 5));
testdata.Push_back(testdatum("val21", "val22", "val23", "val24", 1));
testdata.Push_back(testdatum("val31", "val32", "val33", "val34", 7));

for (std::vector<testdatum>::iterator i = testdata.begin(); i != testdata.end(); ++i) {
    function_in_test(i->argument1, i->argument2, i->argument3, i->argument4m i->count);
}
0
Unacoder

Inspiré par cette réponse très soignée: ( https://stackoverflow.com/a/49572324/4808079 )

Vous pouvez faire des fermetures de lamba:

// Nobody wants to remember the order of these things
struct SomeBigStruct {
  int min = 1;
  int mean = 3 ;
  int mode = 5;
  int max = 10;
  string name;
  string nickname;
  ... // the list goes on
}

.

class SomeClass {
  static const inline SomeBigStruct voiceAmps = []{
    ModulationTarget $ {};
    $.min = 0;  
    $.nickname = "Bobby";
    $.bloodtype = "O-";
    return $;
  }();
}

Ou, si vous voulez être très chic

#define DesignatedInit(T, ...)\
  []{ T ${}; __VA_ARGS__; return $; }()

class SomeClass {
  static const inline SomeBigStruct voiceAmps = DesignatedInit(
    ModulationTarget,
    $.min = 0,
    $.nickname = "Bobby",
    $.bloodtype = "O-",
  );
}

Cela présente certains inconvénients, principalement liés aux membres non initialisés. D'après ce que disent les commentaires liés, il compile efficacement, même si je ne l'ai pas testé. 

Globalement, je pense que c'est une approche soignée.

0
Seph Reed