您遇到了一个案例,该案例在 C++14 完成后立即通过核心问题 1467的解决方案得到解决。
让我们首先注意类foo
是一个聚合。您的代码正在为foo
. 列表初始化的规则在 [8.5.4p3] 中。
在 C++14 中(引自 N4140,最接近已发布标准的工作草案),上面的段落以:
类型的对象或引用的列表初始化T
定义如下:
[...]
因此,如果您的类是聚合类,编译器会尝试进行聚合初始化,但会失败。
这被认为是一个问题,并在工作草案中得到解决。引用当前版本 N4527,上述段落现在以:
类型的对象或引用的列表初始化T
定义如下:
- 如果
T
是一个类类型并且初始化器列表有一个类型为cv U
的元素,其中U
isT
或一个派生自 的类T
,则从该元素初始化对象(对于复制列表初始化,通过复制初始化,对于复制列表初始化,通过直接初始化直接列表初始化)。
- 否则,如果
T
是一个字符数组并且初始化列表有一个元素是一个适当类型的字符串字面量(8.5.2),则按照该部分中的描述执行初始化。
- 否则,如果
T
是聚合,则执行聚合初始化 (8.5.1)。
[...]
您的示例现在属于第一个项目符号所描述的情况,并且使用默认的复制构造函数foo
进行了直接列表初始化(无论它是否为explicit
,因为它是直接初始化)。
也就是说......如果编译器在缺陷报告中实现了解决方案。
- GCC 5.2.0(和 6.0.0 主干)似乎这样做了,但似乎有一个与此相关的错误
explicit
。
- Clang 3.6.0 没有,但 3.8.0 主干可以,而且做得正确(
explicit
没关系)。
- MSVC 14 可以,但 IDE 中的 IntelliSense 没有(下面的波浪线
bar
- 看起来 IntelliSense 使用的 EDG 编译器也没有更新)。
更新:由于编写了此答案,工作草案已通过与问题中的示例和上述解释相关的几种方式进行了进一步修改:
这并没有改变这样一个事实,即在实施所有更改之后,问题中的示例旨在工作,无论是否使用explicit
; 值得知道的是,使它工作的底层机制发生了轻微的变化。
请注意,所有这些更改都是缺陷报告的解决方案,因此它们也应该适用于编译器处于 C++14 和 C++11 模式时。