区分两个零arg构造函数的惯用方法



我有一个这样的类:

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

您可以通过将不初始化构造函数的参数更改为boolint或其他什么来消除伪类,因为它不必再是助记符了。

您还可以交换继承,并使用默认构造函数定义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{} {}
};

最新更新