wake-up-neo.net

C++ - Strukturinitialisierung

Ist es möglich, Strukturen in C++ wie unten angegeben zu initialisieren

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

Die Links hier und hier erwähnen, dass es möglich ist, diesen Stil nur in C zu verwenden. Wenn ja, warum ist dies in C++ nicht möglich? Gibt es einen technischen Grund, warum es nicht in C++ implementiert ist, oder ist es schlecht, diesen Stil zu verwenden? Ich benutze diese Art der Initialisierung gerne, weil meine Struktur groß ist und dieser Stil mir eine klare Lesbarkeit dessen ermöglicht, welcher Wert welchem ​​Mitglied zugeordnet wird.

Bitte teilen Sie mir mit, ob es andere Wege gibt, um dieselbe Lesbarkeit zu erreichen.

Ich habe die folgenden Links vor der Veröffentlichung dieser Frage verwiesen

  1. C/C++ für AIX
  2. C-Strukturinitialisierung mit Variable
  3. Statische Strukturinitialisierung mit Tags in C++
  4. C++ 11 Richtige Strukturinitialisierung
225
Dinesh P.R.

Wenn Sie deutlich machen wollen, was der jeweilige Initialisierungswert ist, teilen Sie ihn einfach in mehrere Zeilen auf und geben Sie jeweils einen Kommentar dazu:

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

Nachdem meine Frage zu keinem befriedigenden Ergebnis geführt hat (da C++ keine tagbasierte Init-Methode für Strukturen implementiert), habe ich den Trick genommen, den ich hier gefunden habe: Sind Mitglieder einer C++ - Struktur standardmäßig auf 0 initialisiert?

Für Sie wäre es das wert:

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

Dies ist sicherlich am nächsten an dem, was Sie ursprünglich wollten (nullen Sie alle Felder mit Ausnahme der Felder, die Sie initialisieren möchten).

87
Gui13

Die Feldbezeichner sind in der Tat eine C-Initialisiersyntax. In C++ geben Sie die Werte in der richtigen Reihenfolge ohne die Feldnamen an. Leider bedeutet dies, dass Sie alle angeben müssen (tatsächlich können Sie nachstehende Felder mit Nullwert weglassen, und das Ergebnis ist dasselbe):

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

Diese Funktion wird als bezeichnete Initialisierer bezeichnet. Es ist eine Ergänzung zum C99-Standard. Diese Funktion wurde jedoch in C++ 11 nicht berücksichtigt. Gemäß der C++ - Programmiersprache, 4. Ausgabe, Abschnitt 44.3.3.2 (C-Funktionen, die nicht von C++ übernommen werden):

Einige Ergänzungen zu C99 (verglichen mit C89) wurden in C++ absichtlich nicht übernommen: 

[1] Arrays mit variabler Länge (VLAs); Verwenden Sie einen Vektor oder eine Form eines dynamischen Arrays 

[2] designierte Initialisierer; Konstruktoren verwenden

Die C99-Grammatik hat die designierten Initialisierer [Siehe ISO/IEC 9899: 2011, N1570-Ausschussentwurf - 12. April 2011]

6.7.9 Initialisierung

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

Andererseits verfügt C++ 11 nicht über die designierten Initialisierer [Siehe ISO/IEC 14882: 2011, N3690-Ausschussentwurf - 15. Mai 2013].

8.5 Initialisierer

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 }
    { }

Um den gleichen Effekt zu erzielen, verwenden Sie Konstruktoren oder Initialisierungslisten:

10

Wie bereits erwähnt, wird dies als Initialisierer bezeichnet.

Diese Funktion ist Teil von C++ 20

7

Sie können einfach über ctor initialisieren:

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

Sie können sogar die Lösung von Gui13 in eine einzige Initialisierungsanweisung packen:

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);

Haftungsausschluss: Ich empfehle diesen Stil nicht

7
user396672

Ich weiß, dass diese Frage ziemlich alt ist, aber ich habe einen anderen Weg gefunden, mit constexpr und currying zu initialisieren:

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);

Diese Methode funktioniert auch für globale statische Variablen und sogar für constexpr-Variablen .. Der einzige Nachteil ist die schlechte Wartbarkeit: Jedes Mal, wenn ein anderes Member mit dieser Methode initialisiert werden muss, müssen alle Member-Initialisierungsmethoden geändert werden.

6
Fabian

Es ist nicht in C++ implementiert. (auch char* strings? Ich hoffe nicht).

Wenn Sie so viele Parameter haben, ist dies normalerweise ein ziemlich schwerer Codegeruch. Aber stattdessen, warum nicht einfach die Struktur wert-initialisieren und dann jedes Mitglied zuweisen?

5
Puppy

Ich könnte hier etwas vermissen, warum nicht:

#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

Ich habe diese Methode für globale Variablen gefunden, für die die ursprüngliche Strukturdefinition nicht geändert werden muss:

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

dann deklarieren Sie die Variable eines neuen Typs, die vom ursprünglichen Strukturtyp übernommen wurde, und verwenden Sie den Konstruktor für die Feldinitialisierung:

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

Nicht ganz so elegant wie der C-Stil ...

Für lokale Variable sollte es auch möglich sein, muss aber geprüft werden, ob ein zusätzliches Memset (dieses, 0, sizeof (* this)) dann notwendig wäre ...

(Beachten Sie, dass 'temp_address' eine Variable vom Typ 'temp_address' ist. Dieser neue Typ erbt jedoch von 'address' und kann an allen Stellen verwendet werden, an denen 'address' erwartet wird, daher ist dies in Ordnung.)

2
VincentP

Dies ist nur möglich, wenn die zu initialisierende Struktur eine POD (plain old data) -Struktur ist. Es darf keine Methoden, Konstruktoren oder auch Standardwerte enthalten.

1
whoKnows

In C++ wurden die Initialisierer im C-Stil durch Konstruktoren ersetzt, die durch Kompilierungszeit sicherstellen können, dass nur gültige Initialisierungen ausgeführt werden (d. H. Nach der Initialisierung sind die Objektelemente konsistent).

Dies ist eine gute Praxis, aber manchmal ist eine Vorinitialisierung praktisch, wie in Ihrem Beispiel. OOP löst dies durch abstrakte Klassen oder kreative Designmuster .

Meines Erachtens bedeutet die Verwendung dieses sicheren Weges die Vereinfachung, und manchmal ist der Kompromiss für die Sicherheit zu teuer, da einfacher Code kein ausgefeiltes Design erfordert, um wartbar zu bleiben.

Als alternative Lösung schlage ich vor, Makros mit Hilfe von Lambdas zu definieren, um die Initialisierung so zu gestalten, dass sie fast wie im C-Stil aussieht:

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

Das ADRESSE-Makro wird erweitert

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

das schafft und nennt das Lambda. Makroparameter sind auch durch Kommas getrennt, daher müssen Sie den Initialisierer in Klammern setzen und wie aufrufen

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

Sie können auch einen generalisierten Makroinitialisierer schreiben

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

aber dann ist der Anruf etwas weniger schön

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

sie können das ADDRESS-Makro jedoch einfach mit dem allgemeinen Makro INIT definieren

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

In GNUC++ (scheint seit 2.5 veraltet zu sein, vor langer Zeit :) Siehe die Antworten hier: C-Strukturinitialisierung mit Labels Es funktioniert, aber wie? ), ist es möglich, eine Struktur wie folgt zu initialisieren:

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

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

Ich hatte heute ein ähnliches Problem, wo ich eine Struktur habe, die ich mit Testdaten füllen möchte, die als Argumente an eine Funktion übergeben werden, die ich teste. Ich wollte einen Vektor dieser Strukturen haben und suchte nach einer Einzeilenmethode, um jede Struktur zu initialisieren.

Ich habe am Ende eine Konstruktorfunktion in der struct gewählt, die meiner Meinung nach auch in ein paar Antworten auf Ihre Frage vorgeschlagen wurde.

Es ist wahrscheinlich eine schlechte Praxis, wenn die Argumente für den Konstruktor dieselben Namen haben wie die öffentlichen Member-Variablen, sodass der Zeiger this verwendet werden muss. Jemand kann eine Bearbeitung vorschlagen, wenn es einen besseren Weg gibt.

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;

Was ich in meiner Testfunktion verwendet habe, um die zu testende Funktion mit verschiedenen Argumenten wie folgt aufzurufen:

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

Inspiriert von dieser wirklich netten Antwort: ( https://stackoverflow.com/a/49572324/4808079 )

Sie können Lamba-Verschlüsse machen:

// 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 $;
  }();
}

Oder wenn Sie sehr schick sein wollen

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

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

Damit sind einige Nachteile verbunden, die meistens mit nicht initialisierten Mitgliedern zu tun haben. Nach den Kommentaren der verknüpften Antworten wird es effizient kompiliert, obwohl ich es nicht getestet habe. 

Alles in allem denke ich, dass es ein ordentlicher Ansatz ist.

0
Seph Reed