为啥我可以从 初始化常规数组,而不是 std::array
Posted
技术标签:
【中文标题】为啥我可以从 初始化常规数组,而不是 std::array【英文标题】:Why can I initialize a regular array from , but not a std::array为什么我可以从 初始化常规数组,而不是 std::array 【发布时间】:2015-09-25 03:24:40 【问题描述】:这行得通:
int arr[10] = ;
arr
的所有元素的值初始化为零。
为什么这不起作用:
std::array<int, 10> arr();
我从 g++(4.8.2 版)收到以下警告:
警告:缺少成员“std::array
::_M_elems”的初始化程序
【问题讨论】:
“这不起作用...我收到以下警告” 所以它起作用了。如果它不起作用,它就不会编译! @JonathanWakely 我相信 OP 试图通过警告来表达惊讶,我也发现它令人惊讶。这确实是有问题的,因为您使用的是-Werror
,我这样做了。很高兴看到警告已在最新版本中删除,但这对那些无法升级的人没有帮助:-(
@ShafikYaghmour,但是 OP 没有使用 -Werror(或者它不会说“警告”)并且在不明智地使用 -Wno-xxxx 的情况下不分青红皂白地使用 -Werror 不一定是好的主意。我知道警告令人惊讶,但它确实有效,所有元素都按预期初始化为零。 (尽管正如 AnT 指出的那样,使用()
很奇怪,应该不鼓励,如果你这样做,孩子们会在街上指着你笑)。
@JonathanWakely 是的,同意,使用-Wno-xxxx
很有用,但在这种情况下,我可能不想使用-Wno-missing-field-initializers
,因为我可能想要它警告的其他情况。同意()
确实很奇怪,但如果将示例更改为std::array<int, 10> arr = ;
,问题仍然存在。
... 并且可以说是一个答案,唯一指出()
很奇怪并不是真正的答案。可能被认为是一个糟糕的答案,因为它现在阻止了对问题的合理编辑,这将简化它并删除不是真正的核心问题。
【参考方案1】:
有两个问题,一个是风格问题和警告。
虽然可能不明显,聚合初始化是在临时发生的,然后用作复制构造函数的参数。更惯用的初始化方法如下:
std::array<int, 10> arr = ;
虽然这仍然会留下警告。
gcc bug report: - -Wmissing-field-initializers relaxation request 覆盖了警告,其中一个 cmets 说:
[...]当然,说 MyType x = ; 的 C++ 语法应该支持, 如此处所示:
http://en.cppreference.com/w/cpp/language/aggregate_initialization
例如在哪里:
struct S int a; float b; std::string str; ; S s = ; // identical to S s = 0, 0.0, std::string;
由于之前的 cmets 中所述的原因,这不应该发出警告。
随后的评论说:
我关于零初始化的说法不准确(谢谢),但是 一般的观点仍然存在:在 C 中你必须写 '= 0' 因为 该语言不支持空大括号初始化程序(您会得到一个 带有-pedantic的警告);在 C++ 中,您可以编写 '= ' 或 'T foo = T();',但你不需要专门写'= 0'。
最新版本的 gcc 不会针对这种情况产生此警告,see it live working with gcc 5.1。
我们可以看到该主题也包含在 thead 中的 Clang 开发人员列表中:-Wmissing-field-initializers。
参考草案 C++11 标准部分 8.5.1
[dcl.init.aggr] 说:
如果列表中的初始化子句少于 聚合中的成员,然后每个成员未显式初始化 应从一个空的初始化列表(8.5.4)初始化。 [ 示例:
struct S int a; const char* b; int c; ; S ss = 1, "asdf" ;
将 ss.a 初始化为 1,将 ss.b 初始化为“asdf”,将 ss.c 初始化为 int() 形式的表达式,即 0。 —end example ]
所以这是有效的 C++,尽管如使用 所指出的那样,它不是有效的 C99。有人可能会争辩说这只是一个警告,但这似乎是使用
进行聚合初始化的惯用 C++,如果我们使用
-Werror
将警告转化为错误,则会出现问题。
【讨论】:
我刚刚在自己的代码中遇到了这个问题,不久前正在做研究。 从你的cmets上看这个问题,这个问题让你有些苦恼。我在下面的回答提供了一个简单的解决方案。【参考方案2】:首先,您可以将()
初始化器与std::array
对象一起使用,但从语义上讲,它代表使用来自临时值初始化的std::array
对象的复制构造函数进行直接初始化,即相当于
std::array<int, 10> arr(std::array<int, 10>);
它实际上应该编译。
其次,当你可以做的时候,你真的不必走()
的方式
std::array<int, 10> arr = ;
或
std::array<int, 10> arr;
两者中的第一个在语法上与您的int arr[10] = ;
最相似,这让我想知道您为什么一开始没有尝试。为什么在构建=
语法的std::array
版本时决定使用()
而不是=
?
【讨论】:
注意,它会编译,它只是一个警告,如果你使用-Werror
,这是一个问题。建议的替代方案都不会删除警告see it live。归根结底,它仍然归结为聚合初始化和一个不必要的警告,说明什么应该被认为是初始化聚合的惯用方式。
因为我(错误地)认为这会调用默认构造函数。看到这个帖子:***.com/questions/31278377/…
@ShafikYaghmour C++ 标准不区分“警告”和“错误”。常见的编译器在这方面也不一致;例如,gcc 会针对某些违反约束以及某些正确代码给出“警告”。因此,某人忽略一条消息是不正确的,因为它“只是一个警告”;问题要先搞清楚。
@MattMcNabb 同意,但不确定这与我的评论有何关联。他们没有违反这里的标准,我也没有说他们违反了,但这是一个令人惊讶的警告,一旦提交错误报告,警告就被删除了。【参考方案3】:
在使用-Werror
编译时,已经有足够多的人指出这是一个“问题”,我认为值得一提的是,如果你只是加倍,问题就会消失:
std::array<int, 10> arr;
在 gcc 4.9.2 上不会对我产生任何警告。
补充一点为什么这可以解决它:我的理解是 std::array 实际上是一个以 C 数组作为其唯一成员的类。所以在大括号上加倍是有意义的:外大括号表示您正在初始化类,然后内大括号默认初始化该类的唯一成员。
由于类中只有一个变量时不会产生歧义,因此只使用一对 应该是合理的,但 gcc 在这里过于迂腐,并发出警告。
【讨论】:
以上是关于为啥我可以从 初始化常规数组,而不是 std::array的主要内容,如果未能解决你的问题,请参考以下文章
为啥使用 std::vector 而不是 realloc? [关闭]
为啥要引入 `std::launder` 而不是让编译器处理它?