![](/img/trans.png)
[英]Initialization of std::array<T,N> in constructor initializer list when T is not default-constructible
[英]Default constructor of T is bypassed when {} are used with new to create std::array<T, n>
让我们考虑这个类:
class A {
public:
A() = delete;
A(int i) :
i_m(i) {
std::cout << __PRETTY_FUNCTION__ << ' ' << i_m << '\n';
}
~A() {
std::cout << __PRETTY_FUNCTION__ << ' ' << i_m << '\n';
}
private:
int i_m{1234567890};
};
默认构造函数被显式删除,因此 AFAIK A
只能从整数构造。 永远不会使用数据成员i_m
的默认初始化。
让我们考虑这个程序:
int main() {
using T = std::array<A, 2>;
//T a;
// error: use of deleted function 'std::array<A, 2>::array()'
// note: 'std::array<A, 2>::array()' is implicitly deleted because the default definition would be ill-formed
// error: use of deleted function 'A::A()'
//T b{};
// error: use of deleted function 'A::A()'
}
同样,这对我来说似乎完全没问题。
现在让我们考虑另一个程序:
int main() {
using T = std::array<A, 2>;
auto foo = new T{};
delete foo;
auto foo_init = new T{1, 2};
delete foo_init;
// auto zorg = new T();
// delete zorg;
// error: use of deleted function 'std::array<A, 2>::array()'
// note: 'std::array<A, 2>::array()' is implicitly deleted because the default definition would be ill-formed:
// error: use of deleted function 'A::A()'
auto zorg_init = new T({3, 4});
delete zorg_init;
}
此代码确实编译(没有警告)并生成以下输出:
A::~A() 0
A::~A() 38870160
A::A(int) 1
A::A(int) 2
A::~A() 2
A::~A() 1
A::A(int) 3
A::A(int) 4
A::~A() 4
A::~A() 3
而现在的东西似乎不罚款给我。 这行auto foo = new T{};
怎么可能auto foo = new T{};
不被视为格式错误? 此处完全绕过了类A
的初始化。
这段代码不编译毫无价值:
int main() {
auto p = new A{};
delete p;
}
错误是预期的:
error: use of deleted function 'A::A()'
我使用以下选项测试了这些代码: -Wall -Wextra -pedantic -std=c++17
。 gcc -v
在我的电脑上给出: gcc version 7.2.0 (x86_64-posix-sjlj-rev0, Built by MinGW-W64 project)
。
任何解释将不胜感激:)
PS:在编译器资源管理器中进行详尽的测试后(使用示例https://godbolt.org/z/5VZLU_ ),这似乎是 gcc 7.x 中的一个问题。 实际上,只有 7.x 版本的 gcc 才能编译此示例。 gcc 6.4 没有。 gcc 8.1 都没有。 叮当。 msvc 都没有。
你能确认这里没有 UB,这真的是一个编译器错误吗?
T b{};
不编译而new T{}
很明显这是一个编译器错误。 然而,标准本身可能有缺陷。 这是为什么。
这个问题的技术答案是:代码格式良好,因为标准明确说明了这一点。 [阵列概览]/2
array
是一个聚合,可以使用最多N
元素进行列表初始化,这些元素的类型可以转换为T
。
初始化器{}
确实包含“最多N
元素,其类型可以转换为T
”——它包含 0 个元素,所有元素都可以转换为A
。
那么元素是如何初始化的呢? 这个标准不是很清楚。 这是[array.overview]/3所说的:
array
满足容器和可逆容器([container.requirements])的所有要求,除了默认构造的array
对象不为空并且swap
没有恒定的复杂性。 [...]
(强调我的)
标准没有提到这个非空array
的元素是如何初始化的。
假设std::array
是这样实现的(对于N == 2
):
template <class T, std::size_t N>
struct array {
T __elem[N];
};
然后根据[dcl.init.aggr]/8从{}
复制初始化元素:
如果列表中的初始化子句少于非联合聚合中的元素,则每个未显式初始化的元素将按如下方式初始化:
( 8.1 ) 如果元素有一个默认的成员初始值设定项([class.mem]),则元素从该初始值设定项进行初始化。
( 8.2 ) 否则,如果元素不是引用,则从空的初始化列表 ([dcl.init.list]) 中复制初始化该元素。
( 8.3 ) 否则,程序格式错误。
[...]
根据[dcl.init.list]/3 格式错误(选择了删除的默认构造函数)。
这意味着标准认为通用实现是错误的,这可能不是故意的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.