私はこのようなクラスを持っています:
_struct event_counts {
uint64_t counts[MAX_COUNTERS];
event_counts() : counts{} {}
// more stuff
};
_
通常、デフォルトの(ゼロ)にしたいように、counts
配列を初期化します。
ただし、プロファイリングによって特定された場所では、配列が上書きされることがわかっているため、配列の初期化を抑制したいのですが、コンパイラーはそれを理解するのに十分なほどスマートではありません。
そのような「セカンダリ」ゼロ引数コンストラクタを作成する慣用的で効率的な方法は何ですか?
現在、私は次のようにダミー引数として渡されるタグクラス_uninit_tag
_を使用しています。
_struct uninit_tag{};
struct event_counts {
uint64_t counts[MAX_COUNTERS];
event_counts() : counts{} {}
event_counts(uninit_tag) {}
// more stuff
};
_
次に、構築を抑制したいときに、event_counts c(uninit_tag{});
のようなno-initコンストラクターを呼び出します。
私は、ダミークラスの作成を含まない、または何らかの方法でより効率的なソリューションなどを受け入れています。
あなたが既に持っている解決策は正しいです、そして私があなたのコードをレビューしているなら私が見たいと思うものと全く同じです。それは可能な限り効率的で、明確で簡潔です。
コンストラクター本体が空の場合、省略またはデフォルトにすることができます。
struct event_counts {
std::uint64_t counts[MAX_COUNTERS];
event_counts() = default;
};
次にデフォルトの初期化event_counts counts;
残します counts.counts
初期化されていません(デフォルトの初期化はここでは何もしません)、そして値の初期化event_counts counts{};
は値を初期化しますcounts.counts
、効果的にゼロで埋めます。
私はあなたの解決策が好きです。ネストされた構造体と静的変数も検討した可能性があります。例えば:
struct event_counts {
static constexpr struct uninit_tag {} uninit = uninit_tag();
uint64_t counts[MAX_COUNTS];
event_counts() : counts{} {}
explicit event_counts(uninit_tag) {}
// more stuff
};
静的変数を使用すると、初期化されていないコンストラクター呼び出しがより便利に思えるかもしれません。
event_counts e(event_counts::uninit);
もちろん、マクロを導入して入力を節約し、より体系的な機能にすることができます
#define UNINIT_TAG static constexpr struct uninit_tag {} uninit = uninit_tag();
struct event_counts {
UNINIT_TAG
}
struct other_counts {
UNINIT_TAG
}
Enumはタグクラスやブール値よりも良い選択だと思います。構造体のインスタンスを渡す必要はなく、どのオプションを取得しているかは呼び出し元から明らかです。
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);
}
}
};
次に、インスタンスの作成は次のようになります。
event_counts e1{};
event_counts e2{event_counts::INIT};
event_counts e3{event_counts::NO_INIT};
または、タグクラスのアプローチに近づけるには、タグクラスの代わりに単一値の列挙を使用します。
struct event_counts {
enum NoInit { NO_INIT };
uint64_t counts[MAX_COUNTERS];
event_counts() : counts{} {}
explicit event_counts(NoInit) {}
};
次に、インスタンスを作成する方法は2つしかありません。
event_counts e1{};
event_counts e2{event_counts::NO_INIT};
私はそれをこのようにします:
_struct event_counts {
uint64_t counts[MAX_COUNTERS];
event_counts() : counts{} {}
event_counts(bool initCounts) {
if (initCounts) {
std::fill(counts, counts + MAX_COUNTERS, 0);
}
}
};
_
event_counts(false)
を使用すると、コンパイラーはすべてのコードをスキップするのに十分なほど賢くなり、クラスのインターフェースを奇妙なものにする代わりに、意味を正確に伝えることができます。
クラスで2フェーズの初期化を検討することをお勧めします。
_struct event_counts {
uint64_t counts[MAX_COUNTERS];
event_counts() = default;
void set_zero() {
std::fill(std::begin(counts), std::end(counts), 0u);
}
};
_
上記のコンストラクターは、配列をゼロに初期化しません。配列の要素をゼロに設定するには、構築後にメンバー関数set_zero()
を呼び出す必要があります。
入力のビットを節約するためだけにサブクラスを使用します。
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{}) {}
};
ニーモニックである必要がないため、初期化しないコンストラクタの引数をbool
またはint
などに変更することで、ダミークラスを取り除くことができます。
また、継承を入れ替えてevents_count_no_init
をEvgのようなデフォルトのコンストラクターで回答で提案し、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{} {}
};