是否可以防止省略聚合初始化成员?
Is it possible to prevent omission of aggregate initialization members?
我有一个结构,其中包含许多相同类型的成员,如下所示
struct VariablePointers {
VariablePtr active;
VariablePtr wasactive;
VariablePtr filename;
};
问题是如果我忘记初始化其中一个结构成员(例如wasactive
(,像这样:
VariablePointers{activePtr, filename}
编译器不会抱怨它,但我会有一个部分初始化的对象。如何防止此类错误?我可以添加一个构造函数,但它会复制变量列表两次,所以我必须键入所有这些三次!
如果有针对 C++11 的解决方案,也请添加C++11个答案(目前我仅限于该版本(。不过,也欢迎更新的语言标准!
这是一个技巧,如果缺少所需的初始值设定项,则会触发链接器错误:
struct init_required_t {
template <class T>
operator T() const; // Left undefined
} static const init_required;
用法:
struct Foo {
int bar = init_required;
};
int main() {
Foo f;
}
结果:
/tmp/ccxwN7Pn.o: In function `Foo::Foo()':
prog.cc:(.text._ZN3FooC2Ev[_ZN3FooC5Ev]+0x12): undefined reference to `init_required_t::operator int<int>() const'
collect2: error: ld returned 1 exit status
警告:
- 在 C++14 之前,这完全可以防止
Foo
成为聚合。 - 这在技术上依赖于未定义的行为(ODR 违规(,但应该适用于任何健全的平台。
对于 clang 和 gcc,您可以使用-Werror=missing-field-initializers
进行编译,将缺少字段初始值设定项的警告转换为错误。 戈博尔特
编辑:对于 MSVC,即使在级别/Wall
似乎也没有发出警告,所以我认为不可能使用此编译器警告缺少初始值设定项。 戈博尔特
我想这不是一个优雅而方便的解决方案......但应该也适用于 C++11 并给出编译时(不是链接时(错误。
这个想法是在结构中添加一个额外的成员,在最后一个位置,一个没有默认初始化的类型(并且不能使用VariablePtr
类型的值(或任何前面值的类型(进行初始化(
通过示例
struct bar
{
bar () = delete;
template <typename T>
bar (T const &) = delete;
bar (int)
{ }
};
struct foo
{
char a;
char b;
char c;
bar sentinel;
};
这样,您将被迫在聚合初始化列表中添加所有元素,包括用于显式初始化最后一个值的值(示例中sentinel
的整数(,否则将收到"调用已删除的'bar'构造函数"错误。
所以
foo f1 {'a', 'b', 'c', 1};
编译和
foo f2 {'a', 'b'}; // ERROR
不。
不幸的是,也
foo f3 {'a', 'b', 'c'}; // ERROR
不编译。
--编辑--
正如 MSalters 所指出的(谢谢(,我的原始示例中存在一个缺陷(另一个缺陷(:bar
值可以用char
值初始化(可转换为int
(,因此可以进行以下初始化
foo f4 {'a', 'b', 'c', 'd'};
这可能会非常令人困惑。
为了避免这个问题,我添加了以下已删除的模板构造函数
template <typename T>
bar (T const &) = delete;
因此,前面的f4
声明给出了编译错误,因为d
值被已删除的模板构造函数截获
对于 CppCoreCheck,有一个规则可以精确地检查是否所有成员都已初始化,并且可以从警告转换为错误 - 当然,这通常是程序范围的。
更新:
您要检查的规则是类型安全Type.6
的一部分:
Type.6:始终初始化成员变量:始终初始化, 可能使用默认构造函数或默认成员初始值设定项。
最简单的方法是不给成员的类型一个 no-arg 构造函数:
struct B
{
B(int x) {}
};
struct A
{
B a;
B b;
B c;
};
int main() {
// A a1{ 1, 2 }; // will not compile
A a1{ 1, 2, 3 }; // will compile
另一种选择:如果你的成员是 const 和 ,你必须初始化所有这些成员:
struct A { const int& x; const int& y; const int& z; };
int main() {
//A a1{ 1,2 }; // will not compile
A a2{ 1,2, 3 }; // compiles OK
如果你能忍受一个虚拟的const和成员,你可以把它与@max66的哨兵的想法结合起来。
struct end_of_init_list {};
struct A {
int x;
int y;
int z;
const end_of_init_list& dummy;
};
int main() {
//A a1{ 1,2 }; // will not compile
//A a2{ 1,2, 3 }; // will not compile
A a3{ 1,2, 3,end_of_init_list() }; // will compile
从 cpp首选项 https://en.cppreference.com/w/cpp/language/aggregate_initialization
如果初始值设定项子句的数量小于 成员或初始值设定项列表完全为空,其余成员 是值初始化的。如果引用类型的成员是其中之一 剩下的成员,该计划格式不正确。
另一种选择是采用max66的哨兵思想,并添加一些语法糖以提高可读性
struct init_list_guard
{
struct ender {
} static const end;
init_list_guard() = delete;
init_list_guard(ender e){ }
};
struct A
{
char a;
char b;
char c;
init_list_guard guard;
};
int main() {
// A a1{ 1, 2 }; // will not compile
// A a2{ 1, init_list_guard::end }; // will not compile
A a3{ 1,2,3,init_list_guard::end }; // compiles OK
- 为什么在C++中首先初始化成员类
- 是否可以防止省略聚合初始化成员?
- 正在复制具有未初始化成员的结构
- 使用大括号或括号初始化成员变量
- C++结构:强制初始化成员?
- C++中未初始化成员布尔变量的默认值是多少?
- 使用默认值初始化成员引用
- 在构造函数中初始化成员时,是否应该在成员上使用 std::move?
- C++使用 { } 初始化成员变量
- 如何根据构造函数参数使用超类类型初始化成员变量?
- OpenGL C++:VBO 的结构包装器不会初始化成员
- 有没有办法在Brace中初始化成员
- 从现有 istream 或类本身创建的 istream 初始化成员 istream
- 从其后声明的另一个成员数据初始化成员数据是否为未定义行为
- 通常应用方法,使用带有构造函数委托的 SFINAE 通过类模板的构造函数初始化成员
- 如何在嵌套类中初始化成员?C
- 是否可以在构造函数主体中初始化成员变量,而不是初始值设定项列表
- 编译器错误,因为构造函数必须显式初始化成员
- 如何使用成员函数初始化成员函数指针
- C 如何使用隐藏的默认构造函数初始化成员