我有一个这样的类:
struct event_counts {
uint64_t counts[MAX_COUNTERS];
event_counts() : counts{} {}
// more stuff
};
通常我想默认(零(初始化counts
数组,如图所示。
然而,在通过分析确定的选定位置,我想取消数组初始化,因为我知道数组即将被覆盖,但编译器不够聪明,无法解决这个问题。
创建这样一个"辅助"零arg构造函数的惯用且有效的方法是什么?
目前,我正在使用一个标记类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{});
这样的无初始化构造函数。
我对不涉及创建虚拟类的解决方案持开放态度,或者在某些方面更高效,等等。
您现有的解决方案是正确的,这正是我在查看您的代码时想要看到的。它尽可能高效、清晰简洁。
如果构造函数主体为空,则可以省略或默认为:
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
}
我认为枚举是比标记类或布尔更好的选择。您不需要传递结构的实例,而且从调用者那里可以清楚地知道您得到的是哪个选项。
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) {}
};
那么只有两种方法可以创建实例:
event_counts e1{};
event_counts e2{event_counts::NO_INIT};
您可能需要为您的类考虑两阶段初始化:
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(bool initCounts) {
if (initCounts) {
std::fill(counts, counts + MAX_COUNTERS, 0);
}
}
};
当你使用event_counts(false)
时,编译器将足够聪明,可以跳过所有代码,你可以准确地说出你的意思,而不是让你的类的接口变得如此奇怪。
我会使用一个子类来节省一些类型:
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{} {}
};