【问题标题】:error C3074: an array can only be initialized with an initializer-list错误 C3074:数组只能用初始化列表初始化
【发布时间】:2014-10-09 10:21:25
【问题描述】:

我正在为 POD、STL 和像数组这样的复合类型开发小型(漂亮)打印机。在这样做的同时,我也在摆弄初始化列表并遇到以下声明

std::vector<double[3]> arr{ { 10, 11, 12 }, { 20, 21, 22 } }; 

似乎 VC2013 和 G++ 4.8 都不太满意,并发出一致的错误消息,这两种情况都对我没有太大帮助

对于 VC++: error C3074: an array can only be initialized with an initialize-list

对于 G++ 4.8: error: array must be initialized with a brace-enclosed initialize

所以这里要么不能使用初始化列表,要么我的语法完全不对?

在类似的方面,以下语法似乎是有效的

std::vector<std::array<int, 3>>  arr{ { 10, 11, 12 }, { 20, 21, 22 } };

我的初始化列表可能有什么问题?

  • 注意 我知道我应该使用 std::array 而不是 C 类型数组,但我只是在试验。
  • 注意如果你不想玩这个,这里有一个IDEONE 版本
  • 注意另外,如果您能将我推荐回标准,那将是非常有益的。

【问题讨论】:

  • 可能是您需要额外一对花括号arr{{{ 10, 11,12}, {20, 21,22}}}的情况之一?
  • @LevLandau:不,这不能解决问题。
  • 根据我使用 VC2010 的经验,它不喜欢初始化器中的多级花括号。尝试移除除最外层之外的所有大括号。这对我很有用,虽然我不记得曾在模板类中使用过它。
  • @Logicrat:实际上我使用的是应该支持统一初始化列表的 VC2013。我已经用 g++ 进行了交叉检查
  • 向量要求类型是可移动的和可移动的。

标签: c++ arrays visual-c++ c++11 initialization-list


【解决方案1】:

阅读current C++1y draft standard

从表 99 之前:

T 是 EmplaceConstructible 进入 X 从 参数 , 对于零个或多个参数 参数 , 意味着 以下表达式是格式良好的: allocator_traits::construct(m, p, args)

表 100:

X(il);              |  Equivalent to      | X(il.begin(), il.end());
--------------------+---------------------+--------------------------------
X(i, j);            |                     | Requires:
X a(i, j);          |                     | T shall be EmplaceConstructible
                                          | into X from *i.

所以std::vector&lt;double[3]&gt; v{ {1,2,3}, {4,5,6} }; 有效当且仅当double[3]EmplaceConstructible{1,2,3} 作为传递给std::vector&lt;double[3]&gt; 的初始值设定项列表的元素。

还有一个关于前向迭代器的子句,但这没问题(因为std::initialzier_list 迭代器是前向迭代器)。

std::vector&lt;T&gt; 采用std::initializer_list&lt;T&gt; 参数。

所以std::initializer_list&lt;double[3]&gt;是候选名单。

首先,std::initializer_list&lt;double[3]&gt; x = {{1.0, 2.0, 3.0}}; 在 gcc 中编译失败。但假设这是 gcc 中的一个错误。

第二,::new (nullptr) double[3](std::initializer_list&lt;double&gt;{1.0, 2.0, 3.0});placement new,EmplaceConstructable 在缺少合适的construct 覆盖时简化为无法编译。

所以double[3] 不是EmplaceConstruble 来自std::initalizer_list&lt;double&gt; 也不是来自double[3] 也不是其他任何东西(因为发生错误是因为我使用了括号,而不是因为括号中的内容,在放置新),除非分配器使用我不知道的魔法来避免放置 new。

因此,您的代码违反了当前的草案标准,可能是 C++11,当然还有 C++03(对容器有更严格的要求)。

【讨论】:

  • 可能是我在这里遗漏了一些重要的东西,但类似地,如果我编码std::vector&lt;double[3]&gt; arr; arr[0][0] = 1; 为什么编译器不会抱怨
  • @Abhijit 未定义行为;编译器可以做任何事情,包括不抱怨。
  • @AlanStokes:我不这么认为 Yakk 提到它是一个 UB,但我想听听 Yakk 的回复。
  • C++11 移除了对容器模板参数的全局要求,现在指定了对需要从其元素中获得这些行为的各个容器函数的要求。
  • @Abhijit 答案是用当前标准草案修改的,而不是我对 C++03 标准的记忆。
【解决方案2】:

这是 gcc 和 MSVC 中的错误; clang 可以正确编译您的代码。

最新版本的 gcc 实际上使编译器崩溃(“ice”):

内部编译器错误:树检查:预期类“类型”,在 useless_type_conversion_p 中有“异常”(错误标记),位于 tree-ssa.c:1189

标准相当明确;来自[dcl.init.list]

5 - 一个std::initializer_list&lt;E&gt; 类型的对象是从一个初始化列表构造的,就好像实现分配了一个N 类型为E 的元素的数组,其中N 是初始化列表中的元素数。该数组的每个元素都使用初始值设定项列表的相应元素进行复制初始化,并且构造 std::initializer_list&lt;E&gt; 对象以引用该数组。 [...]

改编该段落中的示例:

using E = double[3];
using X = std::vector<E>;
E __a[2] = {{10, 11, 12}, {20, 21, 22}};
X x(__a, __a+2);

不过,这有点作弊;更仔细的翻译会写E __a[2] = {E{10, 11, 12}, E{20, 21, 22}};,这是无效的。但确实可以从花括号初始化列表中复制初始化数组double[3]E __a0 = {10, 11, 12};

【讨论】:

  • This is a bug in gcc and MSVC。这是一个记录在案的错误吗?另外,我很不确定 [dcl.init.list] 是否证明语法合理。此外,我认为,仅仅因为 clang 编译,我们可以称它为错误(但这正是我的想法,我可能错了:-))
  • 嗯。如果我们用E 包裹内部的braced-init-lists,那么我们会得到与gcc 相同的错误。但肯定可以从一个花括号初始化列表中复制初始化数组double[3]E __a0 = {10, 11, 12}
  • @Abhijit:Clang 也有错误。你不能仅仅因为它在 Clang 中编译就认为它是 GCC 中的一个错误。
  • 这不是关于从花括号初始化器列表构造double[3](尽管这是一步),您还需要从double[3] 构造的emplace。而且我认为不是。
  • double[3] 甚至不能合法地成为容器的value_type,因为它甚至不是Erasable。该规范导致了伪析构函数调用,它的格式不正确,因为伪析构函数调用仅允许用于标量类型。
猜你喜欢
  • 2011-06-05
  • 2011-09-14
  • 1970-01-01
  • 2014-03-01
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多