初始化不可移动对象数组:为什么这样的代码无法在 GCC 上编译?
Initialization of an array of non-moveable objects: why does such code fail to compile on GCC?
下面是一个代码示例,其中Test
是一个不可复制和不可移动的类,具有一些virtual
成员和一个用户定义的构造函数,B
是一个包含Test
对象的原始(C 样式(数组的类:
class Test
{
public:
Test() = delete;
Test(const Test&) = delete;
Test(Test&&) = delete;
Test& operator=(const Test&) = delete;
Test& operator=(Test&&) = delete;
Test(int a, int b) : a_(a), b_(b) {}
virtual ~Test() {}
int a_;
int b_;
};
//----------------
class B
{
public:
/*(1)*/ B() : test_{{1, 2}, {3, 4}} {} // Does not compile on GCC, but compiles on Clang and MSVC
private:
Test test_[2];
};
//----------------
int main()
{
B b;
/*(2)*/ Test test[2] = {{1, 2}, {3, 4}}; // Successfully compiles on GCC, Clang and MSVC
}
我想使用支撑初始化语法(第/*1*/
行(初始化B
的内部数组test_
,以便就地构造两个Test
对象中的每一个,而无需创建一个临时然后移动它。
在 Clang 和 MSVC 上,此代码编译时没有警告。
但是GCC的行为让我感到困惑:它无法编译行/*1*/
,同时成功编译行/*2*/
,我使用相同的语法来初始化本地数组。然而,对于编译第一行,它仍然需要类Test
的已删除移动构造函数。
问题是,为什么?C++标准是否明确定义了这些行是否应该编译/*1*/
和/*2*/
?如果是这样,从标准的角度来看,哪个编译器是正确的?这种不一致的行为是否可以称为GCC错误,或者Clang和MSVC是否忽略了它们应该执行的一些检查?
我可以理解 GCC 可能需要一个移动构造函数才能从内部大括号 ({1, 2}
( 创建一个临时Test
对象,然后将该对象移动到数组中。因此编译错误。但如果是这样,为什么它没有因为同样的原因而/*(2)*/
失败呢?在这个例子中,这是我最困惑的事情。
顺便说一下,这里有一个有趣的观察:如果我用std::array<Test, 2>
(而不是"C 样式"数组(替换test_
的定义,并将构造函数初始化列表中的代码替换为test_{{{1, 2}, {3, 4}}}
,一切都开始在所有提到的三个编译器上成功编译。
我也不清楚为什么 GCC 在这种情况下在任何一行上都没有失败,而"原始"数组却失败了。
谁能解释一下?
我认为初始化没有问题,所以我认为这是一个 GCC 错误。
所涉及的初始化是列表初始化,所以我们咨询 [dcl.init.list]/3:
定义了
T
类型的对象或引用的列表初始化 如下:
[...]
(3.3( 否则,如果
T
是一个聚合,则聚合初始化为 执行。[...]
(数组是一个聚合。 现在我们转到 [dcl.init.aggr]/3:
当聚合由指定的初始值设定项列表初始化时 在 [dcl.init.list] 中,初始值设定项列表的元素被视为 聚合元素的初始值设定项,按顺序排列。每 元素从相应的初始值设定项子句。如果初始值设定项子句是表达式,并且需要缩小转换才能转换表达式,则 程序格式不正确。
因此,对于这两个元素中的任何一个,我们实际上是在做Test a = {1, 2}
,这是有效的,因为Test(int, int)
不是显式的。 因此,初始化的格式正确,编译器应接受。
- GCC对可能有效的代码抛出init list生存期警告
- GCC 和 Clang 代码性能的巨大差异
- GCC 能否优化具有相同主体的函数的代码大小?
- GCC 编译器是否应该对涉及 [[fallthrough]] 属性的格式错误的C++代码进行诊断?
- 如何使用 gcc 通过命令行限制C++代码的内存使用量?
- MSVC对这段代码很满意,但GCC并不那么热衷
- 为什么这段代码无法使用 gcc 4.8.5 编译,而使用 clang 编译正常
- 为什么以下代码在在线 ide(gcc 7.2.0) 上有效,但在 ubuntu 上出现错误?
- Boost tread 代码在 VC++ 下编译,但不在 GCC 下编译:如何获得更好的调试信息
- gcc 可以使用较旧的第三方库编译 C++17 代码吗?
- 如何使用 gcc 内联汇编器代码访问成员变量
- 初始化不可移动对象数组:为什么这样的代码无法在 GCC 上编译?
- 是否可以使用 GCC 编译具有特定编译器标志的代码文件的一部分?
- variadic模板代码中的GCC VS MSVC编译误差
- Clang vs GCC:挥发性访问的不同代码
- 为什么 GCC 6.3 在没有显式 C++11 支持的情况下编译此大括号初始化列表代码
- GCC 带内联汇编和 -Ofast 为内存操作数生成额外的代码
- 为什么以下代码不使用GCC编译,而是用Clang编译罚款
- 相同的代码在不同的 gcc 编译器中存在巨大的性能差异
- 非常量引用类型的无效初始化中出错.在Linux操作系统中使用c++代码.GCC编译器