【问题标题】:Clang warns me when I aggregate initialize an array while gcc doesn't当我聚合初始化一个数组而 gcc 没有时,Clang 警告我
【发布时间】:2015-09-06 17:19:34
【问题描述】:

当我用 CLANG 编译以下代码时:

#include <iostream>
#include <array>
#include <algorithm>
#include <functional>

int main() {
  std::array<int, 2> a = {1, 2};
  std::array<int, 2> b = {2, 1};
  std::array<int, 2> c;
  std::transform(a.begin(), a.end(), b.begin(), c.begin(), std::multiplies<int>());
  for(auto &&i : c) std::cout << i << " ";
  std::cout << std::endl;
}

通过发出命令:

clang++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp

它发出警告:

警告:建议在子对象初始化周围使用大括号 [-Wmissing-braces]

CLANG DEMO

但是,GCC 编译该程序时根本不会发出警告。

GCC DEMO

问:

  1. 哪个编译器是正确的?
  2. Clangs 警告我的原因是什么?

【问题讨论】:

  • 这是一个警告;他们都是对的。 GCC 还使用-Wmissing-braces 发出警告。
  • 有一篇很好的回顾帖子,涵盖了={{ 等所有可能的组合,但我现在找不到。令人烦恼的是,std::array 不需要在内部使用 C 样式的数组,因此双括号版本不正确。

标签: c++ c++11 gcc clang stdarray


【解决方案1】:

在某些情况下,可以省略大括号。这是其中一种情况。用于初始化ab 的最外层大括号是可选的。无论哪种方式,它在语法上都是正确的 - 但只包含它们会更清楚。 Clang 只是警告你(警告,而不是错误) - 这是一个完全有效的警告。正如chris 所指出的,与-Wmissing-braces 一样,gcc 会发出相同的警告。最终,两个编译器都接受了代码,这是正确的;毕竟,它是一个有效的程序。这才是最重要的。

来自 [dcl.init.aggr]:

可以在 initializer-list 中省略大括号,如下所示。如果 initializer-list 以左大括号开头,则 initializer-clauses 的后续逗号分隔列表初始化子聚合的成员;它是 initializer-clauses 比成员多是错误的。但是,如果子聚合的 initializer-list 不以左大括号开头,则列表中只有足够的 initializer-clauses 被带到 初始化子聚合的成员;剩下的任何 initializer-clauses 都留给初始化下一个 当前子聚合是其成员的聚合的成员。 [示例:

float y[4][3] = {
    { 1, 3, 5 },
    { 2, 4, 6 },
    { 3, 5, 7 },
};

是一个完全支撑的初始化:135初始化数组y[0]的第一行,即y[0][0]y[0][1]y[0][2]。同样,接下来的两行初始化y[1]y[2]。初始化器提前结束并且 因此y[3]s 元素的初始化就像使用float() 形式的表达式显式初始化一样, 也就是说,用0.0 初始化。在以下示例中,省略了 initializer-list 中的大括号;然而 initializer-list 和上面例子中完全支撑的initializer-list 效果一样,

float y[4][3] = {
    1, 3, 5, 2, 4, 6, 3, 5, 7
};

y 的初始化程序以左大括号开头,但 y[0] 的初始化程序没有,因此三个元素来自 该列表被使用。同样,接下来的三个依次为y[1]y[2]——结束示例]

【讨论】:

    【解决方案2】:

    哪个编译器是正确的?

    两个编译器都是正确的。 Brace-elision 是一种允许由一对大括号初始化聚合的功能。每个成员子对象都根据需要使用尽可能多的 initializer-clauses 进行初始化。这是为了允许更方便的初始化形式。

    Clangs 警告我的原因是什么?

    Clang 通过警告您很有帮助,因为虽然您可以省略大括号,但如果您不小心,聚合将如何初始化并不总是很清楚。您必须确定哪些初始化子句与哪些成员子对象相关。

    【讨论】:

    • N3526 从未被采用。大括号省略自 C++98 以来一直存在; CWG 1270 在 C++14 中稍微放宽了规则,但 OP 中的代码在任何一组规则下都是有效的。
    • @T.C.我认为它被采用了,因为我们显然在 C++14 中有这个。为什么该提案会被拒绝,但会考虑 cwg 问题?
    • 不,你没有那篇论文的提议,用 C++14 或其他语言。 (std::array&lt;int, 2&gt; a = { {1}, {2} }; 是一个错误。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2012-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多