此代码与GCC 5.X,MSVC编译良好,但GCC 6.X给出错误:
"转换为' a'从初始化列表将使用显式 构造函数' a :: a()'" ,clang "选择的构造函数是明确的 复制初始化"
删除explicit
或更改为a c{}
可以解决问题,但我很好奇为什么会这样。
class a
{
public:
explicit a () {}
};
struct b
{
a c;
};
int main() {
b d{};
}
答案 0 :(得分:22)
b
是aggregate。使用初始化列表初始化它时,列表中的元素将初始化聚合的第一个 n 成员,其中 n 是列表中元素的数量。聚合的其余元素是 copy-list-initialized 。
因此,在您的示例中,c
将是 copy-list-initialized ,但如果选择的构造函数为explicit
,那么这是错误的,因此错误。< / p>
相关标准报价为
当聚合由[dcl.init.list]中指定的初始化列表初始化时,初始化列表的元素将被视为聚合元素的初始值。 聚合的显式初始化元素确定如下:
...... - 如果初始化列表是初始化列表,则聚合的显式初始化元素是聚合的第一个 n 元素,其中 n 是初始化列表中元素的数量 - 否则,初始化列表必须为{}
,并且没有显式初始化的元素。
对于非联合聚合,每个不是显式初始化元素的元素初始化如下:
...... - 否则,如果元素不是引用,则从空的初始化列表([dcl.init.list])复制初始化该元素。
中描述了从空初始化列表中复制初始化
c
的效果
类型
T
的对象或引用的列表初始化定义如下:
...... - 否则,如果初始化列表没有元素且T
是具有默认构造函数的类类型,则该对象是值初始化的。
value-initialize
T
类型的对象意味着:
...... - 如果T
是一个(可能是cv限定的)类类型,没有默认构造函数([class.ctor])或者是用户提供或删除的默认构造函数,那么该对象是默认初始化的; < / p>
默认初始化
T
类型的对象意味着:
- 如果T是(可能是cv限定的)类类型,则考虑构造函数。枚举适用的构造函数([over.match.ctor]),并通过重载决策选择初始化程序()的最佳构造函数。使用空参数列表调用如此选择的构造函数来初始化对象。
...对于复制初始化,候选函数是该类的所有转换构造函数。
在没有函数说明符
explicit
的情况下声明的构造函数指定从其参数类型(如果有)到其类的类型的转换。这样的构造函数称为转换构造函数。
在上面的示例中,a
没有转换构造函数,因此重载解析失败。 [class.conv.ctor] / 2 中的(非规范)示例甚至包含一个非常类似的案例
struct Z { explicit Z(); explicit Z(int); explicit Z(int, int); }; Z c = {}; // error: copy-list-initialization
您可以通过为c
struct b
{
a c{}; // direct-list-initialization, explicit ctor is OK
};