c++ - Idiomatische Methode zur Unterscheidung zweier Null-Arg-Konstruktoren




performance constructor (5)

Die Lösung, die Sie bereits haben, ist korrekt und genau das, was ich sehen möchte, wenn ich Ihren Code überprüfe. Es ist so effizient wie möglich, klar und präzise.

Ich habe eine Klasse wie diese:

struct event_counts {
    uint64_t counts[MAX_COUNTERS];

    event_counts() : counts{} {}

    // more stuff

};

Normalerweise möchte ich standardmäßig (Null) das Zählungsarray wie gezeigt initialisieren.

An ausgewählten Stellen, die durch die Profilerstellung identifiziert wurden, möchte ich jedoch die Array-Initialisierung unterdrücken, da ich weiß, dass das Array im Begriff ist, überschrieben zu werden, der Compiler jedoch nicht klug genug ist, dies herauszufinden.

Was ist eine idiomatische und effiziente Möglichkeit, einen solchen "sekundären" Null-Argument-Konstruktor zu erstellen?

Derzeit verwende ich eine Tag-Klasse uninit_tag die wie folgt als Dummy-Argument übergeben wird:

struct uninit_tag{};

struct event_counts {
    uint64_t counts[MAX_COUNTERS];

    event_counts() : counts{} {}

    event_counts(uninit_tag) {}

    // more stuff

};

Dann rufe ich den Konstruktor no-init wie event_counts c(uninit_tag{}); wenn ich den Bau unterdrücken will.

Ich bin offen für Lösungen, bei denen keine Dummy-Klasse erstellt wird oder die in irgendeiner Weise effizienter sind usw.


Ich denke, eine Aufzählung ist eine bessere Wahl als eine Tag-Klasse oder ein Bool. Sie müssen keine Instanz einer Struktur übergeben und es wird dem Aufrufer klar, welche Option Sie erhalten.

struct event_counts {
    enum Init { INIT, NO_INIT };
    uint64_t counts[MAX_COUNTERS];

    event_counts(Init init = INIT) {
        if (init == INIT) {
            std::fill(counts, counts + MAX_COUNTERS, 0);
        }
    }
};

Das Erstellen von Instanzen sieht dann so aus:

event_counts e1{};
event_counts e2{event_counts::INIT};
event_counts e3{event_counts::NO_INIT};

Ich würde eine Unterklasse verwenden, um ein bisschen Tipparbeit zu sparen:

struct event_counts {
    uint64_t counts[MAX_COUNTERS];

    event_counts() : counts{} {}
    event_counts(uninit_tag) {}
};    

struct event_counts_no_init: event_counts {
    event_counts_no_init(): event_counts(uninit_tag{}) {}
};

Sie können die Dummy-Klasse loswerden, indem Sie das Argument des nicht initialisierenden Konstruktors in bool oder int oder so ändern, da es nicht mehr mnemonisch sein muss.

Sie können auch die Vererbung vertauschen und events_count_no_init mit einem Standardkonstruktor wie Evg definieren, der in der Antwort vorgeschlagen wird, und dann events_count als Unterklasse events_count :

struct event_counts_no_init {
    uint64_t counts[MAX_COUNTERS];
    event_counts_no_init() = default;
};

struct event_counts: event_counts_no_init {
    event_counts(): event_counts_no_init{} {}
};

Ich würde es so machen:

struct event_counts {
    uint64_t counts[MAX_COUNTERS];

    event_counts() : counts{} {}

    event_counts(bool initCounts) {
        if (initCounts) {
            std::fill(counts, counts + MAX_COUNTERS, 0);
        }
    }
};

Der Compiler ist intelligent genug, um den gesamten Code zu überspringen, wenn Sie event_counts(false) , und Sie können genau sagen, was Sie meinen, anstatt das Interface Ihrer Klasse so verrückt zu machen.


Wenn der Konstruktorkörper leer ist, kann er weggelassen oder als Standard festgelegt werden:

struct event_counts {
    std::uint64_t counts[MAX_COUNTERS];
    event_counts() = default;
};

Dann zählt die Standardinitialisierung event_counts counts; counts.counts uninitialisiert (Standardinitialisierung ist hier ein No-Op), und die event_counts counts{}; wird den Wert initialize counts.counts und effektiv mit Nullen füllen.







constructor