我正在尝试将旧的 C++03 代码库迁移到 C++11。但我无法理解 gcc 在以下情况下警告我的内容:
% g++ -std=c++03 t.cxx
% g++ -std=c++11 t.cxx
t.cxx: In function ‘int main()’:
t.cxx:8:21: warning: converting to ‘A’ from initializer list would use explicit constructor ‘A::A(int)’
8 | int main() { B b = {}; }
| ^
t.cxx:8:21: note: in C++11 and above a default constructor can be explicit
struct A {
explicit A(int i = 42) {}
};
struct B {
A a;
};
int main() {
B b = {};
return 0;
}
我在这里尝试做的是基本的零初始化。这对于 C++03 似乎是合法的,但我无法理解如何在 C++11 中表达等价物。
仅供参考,我正在使用:
% g++ --version
g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
给定的程序是格式错误,原因如下所述。
B
是一个聚合。由于您没有显式初始化a
,因此dcl.init.aggr#5适用:
- 对于非联合聚合,每个不是显式初始化元素的元素都按如下方式初始化:
5.2 否则,如果该元素不是引用,则该元素会从空初始化器列表复制初始化([dcl.init.list])。
这意味着
a
是从空初始化器列表中复制初始化的。换句话说,就好像我们在写:
A a = {}; // not valid see reason below
现在我们进入dcl.init.list#3.5:
否则,如果初始值设定项列表没有元素并且 T 是具有默认构造函数的类类型,则该对象是 值初始化。
这意味着该对象将被初始化。
现在进行值初始化:
对 T 类型的对象进行值初始化意味着:
- 如果 T 是一个(可能是 cv 限定的)类类型 ([class]),那么
- 如果 T 没有默认构造函数 ([class.default.ctor]) 或用户提供或删除的默认构造函数,则该对象是 default-initialized;
所以我们进入默认初始化:
如果 T 是一个(可能是 cv 限定的)类类型 ([class]),则考虑构造函数。枚举适用的构造函数 ([over.match.ctor]),初始化器 () 的最佳构造函数是通过重载决策([over.match])选择。 使用空参数列表调用如此选择的构造函数来初始化对象。
最后来自over.match.ctor:
当类类型的对象被直接初始化、从相同或派生类类型 ([dcl.init]) 的表达式复制初始化或默认初始化时,重载决策会选择构造函数。 对于不在复制初始化上下文中的直接初始化或默认初始化,候选函数是正在初始化的对象的类的所有构造函数。 对于复制初始化(包括复制初始化上下文中的默认初始化),候选函数是该类的所有转换构造函数([class.conv.ctor])。 参数列表是初始化器的表达式列表或赋值表达式。
这意味着只有转换因子才是候选者。由于
A::A(int)
是显式的,它不是一个转换向量,因此候选集是空的,并且程序(A a ={};
)格式不正确。
本质上,失败的原因是
A a = {};
格式不正确。
为了解决这个问题,我们可以在列表中传递
A{}
或 A{0}
作为初始化器,如下所示:
B b = { A{} }; //ok now
B c = { A{0} }; //also ok
工作演示。
请注意,另一方面,编写
A a{};
是格式良好的,因为这是一个直接初始化上下文,因此它是 direct-list-initialization。