#include<cstddef>
template<typename T, std::size_t N>
struct A {
T m_a[N];
A() : m_a{} {}
};
struct S {
explicit S(int i=4) {}
};
int main() {
A<S, 3> an;
}上面的代码在MSVC (2017)中编译得很好,但是clang3.8.0(clang++ --version && clang++ -std=c++14 -Wall -pedantic main.cpp的输出)却失败了:
clang version 3.8.0 (tags/RELEASE_380/final 263969)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/bin
main.cpp:6:15: error: chosen constructor is explicit in copy-initialization
A() : m_a{} {}
^
main.cpp:14:13: note: in instantiation of member function 'A<S, 3>::A' requested here
A<S, 3> an;
^
main.cpp:10:14: note: constructor declared here
explicit S(int i=4) {}
^
main.cpp:6:15: note: in implicit initialization of array element 0 with omitted initializer
A() : m_a{} {}
^
1 error generated.clang 5.0还拒绝编译以下内容:
<source>:6:17: error: expected member name or ';' after declaration specifiers
A() : m_a{} {}
^
<source>:6:14: error: expected '('
A() : m_a{} {}
^
2 errors generated.如果我在A的构造函数中使用简单括号来(即A() : m_a() {}),它会编译得很好。从优先选择中,我会怀疑两者都应该导致相同的结果(即值初始化)。我是遗漏了什么,还是这是编译器中的一个bug?
发布于 2018-02-19 20:50:53
对于m_a{}
m_a上执行聚合初始化。
初始化器的语义如下。..。- If the initializer is a (non-parenthesized) _braced-init-list_ or is `=` _braced-init-list_, the object or reference is list-initialized.
- [...]列表-对象的初始化或T类型的引用定义如下:
- [...]
- Otherwise, if `T` is an aggregate, aggregate initialization is performed.
- [...]{} )中复制-初始化m_a的每个元素。
对于不合并聚合,未显式初始化元素的每个元素初始化如下:- [...]
- Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).
- [...]T类型的引用定义如下:- [...]
- Otherwise, if the initializer list has no elements and `T` is a class type with a default constructor, the object is value-initialized.
- [...]T类型的对象进行值初始化,意味着:- if `T` is a (possibly cv-qualified) class type with either no default constructor ([class.ctor]) or a default constructor that is user-provided or deleted, then the object is default-initialized;
- [...]()上执行过载解析;
默认情况下-初始化T类型的对象意味着:- If `T` is a (possibly cv-qualified) class type, constructors are considered. The applicable constructors are enumerated ([over.match.ctor]), and the best one for the _initializer_ `()` is chosen through overload resolution. The constructor thus selected is called, with an empty argument list, to initialize the object.
- [...]对于m_a()
- [...]
- If the initializer is `()`, the object is value-initialized.
- [...]T类型的对象进行值初始化,意味着:- [...]
- if `T` is an array type, then each element is value-initialized;
- [...]()执行过载解析;发布于 2018-02-19 17:26:50
嘎嘎声是对的。
你的困惑来自:
从优先选择中,我会怀疑两者都应该导致相同的结果(即值初始化)。
不,它们有不同的效果。请注意该页的说明:
在所有情况下,如果使用空大括号{}且T是聚合类型,则执行聚合初始化而不是值初始化。
这意味着当使用大括号-init-list初始化时,对于聚合类型,首选执行聚合初始化。使用A() : m_a{} {}时,m_a是一个属于集料类型的数组,然后执行聚合初始化:
(强调地雷)
按照类定义中数组下标/外观的顺序,每个copy-initialized数组元素或非静态类成员都是初始化程序列表对应子句中的
direct public base, (since C++17)。
和
如果初始化器子句的数量小于成员
and bases (since C++17)或初始化程序列表的数目完全为空,则其余成员and bases (since C++17)将按照通常的列表初始化规则(对具有默认构造函数的非类类型和非聚合类执行值初始化,以及聚合的聚合初始化),由空列表初始化by their default initializers, if provided in the class definition, and otherwise (since C++14)。
这意味着,其余的元素,即m_a的所有3个元素都将从空列表中复制初始化;对于空列表,将考虑S的默认构造函数,但它被声明为explicit;复制初始化不会调用explicit构造函数:
复制列表初始化(同时考虑显式和非显式构造函数,但只能调用非显式构造函数)
另一方面,A() : m_a() {}执行值初始化,然后
3)如果T是数组类型,则数组中的每个元素都是值初始化的;
然后
1)如果T是一个没有默认构造函数的类类型,或者是用户提供或删除的默认构造函数,则该对象是默认初始化的;
然后调用S的默认构造函数来初始化m_a的元素。它是否是explicit对默认初始化来说并不重要。
发布于 2018-02-19 17:03:07
这显然是标准的错误(问题是,为什么?):
m_a{}列表-初始化S::m_a
[dcl.init.list]/1列表初始化是从大括号内的列表中初始化对象或引用.这样的初始化器称为初始化程序列表,指定初始化程序列表的逗号分隔的初始化程序-子句或指定-初始化-子句称为初始化程序列表的元素。初始化程序列表可能为空。列表初始化可以在直接初始化或复制初始化上下文中进行;直接初始化上下文中的列表初始化称为直接列表初始化,副本初始化上下文中的列表初始化称为复制列表初始化。
作为数组,A<S, 3>::m_a是聚合类型([dcl.init.aggr]/1)。
[dcl.init.aggr]/3.3
{},并且没有显式初始化元素。下面,因为没有显式初始化的元素
[dcl.init.aggr]/5.2
[dcl.init.list])复制初始化的。然后,每个S of A<S, 3>::m_a都被复制初始化。
[dcl.init]/17.6.3
由于S的默认构造函数是显式的,因此它不能从源类型转换为目标类型(S)。
另一方面,使用m_a()的语法不是聚合成员初始化,也不调用复制初始化。
https://stackoverflow.com/questions/48870550
复制相似问题