【问题标题】:Initializing std::array<char,x> member in constructor using string literal. GCC bug?使用字符串文字在构造函数中初始化 std::array<char,x> 成员。海湾合作委员会错误?
【发布时间】:2014-02-04 14:21:30
【问题描述】:

以下示例在构造函数中使用字符串字面量初始化 std::array &lt;char, N&gt; 成员不会在 GCC 4.8 上编译,而是使用 Clang 3.4 编译。

#include <iostream>
#include <array>

struct A {
  std::array<char, 4> x; 
  A(std::array<char, 4> arr) : x(arr) {}
};


int main() {
    // works with Clang 3.4, error in GCC 4.8.
    // It should be the equivalent of "A a ({'b','u','g','\0'});"
    A a ({"bug"});
    for (std::size_t i = 0; i < a.x.size(); ++i)
        std::cout << a.x[i] << '\n';

    return 0;
}

在第一印象中,它看起来像一个 GCC 错误。我觉得它应该可以编译,因为我们可以直接用字符串初始化 std::array&lt;char, N&gt;。例如:

std::array<char, 4> test = {"bug"}; //works

我很想看看标准对此有何评论。

【问题讨论】:

  • 使用 clang 3.5 (trunk 200501) 和 -Wall -Wextra 干净地编译并按预期工作。 error messages with gcc online.
  • FWIW, A a (std::array&lt;char, 4&gt;{"bug"}); 被 GCC 接受,因此“没有已知的参数 1 从 '' 到 'std::array' 的转换”看起来对我来说很奇怪。
  • 我不明白为什么 {{"bug"}} 有效,您要求的是 4 大小的 char 数组,而不是字符串(这是另一种野兽)。也许clang很聪明并修复了用户的代码
  • 标准中有一条特殊规则允许您从字符串文字初始化char[N]。我认为这条规则正在发挥作用。 See here.

标签: c++ gcc c++11 compiler-bug stdarray


【解决方案1】:

是的,您的代码是有效的;这是 gcc 中的一个错误。

这是一个演示错误的更简单程序(我已将std::array&lt;char, 4&gt; 替换为S 并删除了A,因为我们可以在函数返回中演示错误(这使得分析更简单,因为我们不必担心构造函数重载):

struct S { char c[4]; };
S f() { return {"xxx"}; }

这里我们有一个 S 类型的目标对象,它是 copy-initialized (8.5p15) 来自 braced-init-list {"xxx"},所以对象被列表初始化 (8.5p17b1)。 S 是一个聚合 (8.5.1p1),因此执行聚合初始化 (8.5.4p3b1)。在聚合初始化中,成员 c 从相应的 initializer-clause "xxx" (8.5.1p2) 中复制初始化。我们现在返回 8.5p17,目标对象类型为 char[4],并初始化字符串文字 "xxx",因此 8.5p17b3 将我们指向 8.5.2,char 数组的元素由字符串的连续字符初始化(8.5.2p1)。

请注意,gcc 可以使用复制初始化S s = {"xxx"};,同时打破各种形式的复制和直接初始化;参数传递(包括构造函数)、函数返回、基数和成员初始化:

struct S { char c[4]; };
S f() { return {"xxx"}; }
void g(S) { g({"xxx"}); }
auto p = new S({"xxx"});
struct T { S s; T(): s({"xxx"}) {} };
struct U: S { U(): S({"xxx"}) {} };
S s({"xxx"});

最后一个特别有趣,因为它表明这可能与bug 43453有关。

【讨论】:

  • 您确定标准特别对待array&lt;char, N&gt;,因为它允许使用大括号括起来的字符串文字进行初始化吗? IIRC,它没有指定内部表示,所以在我看来,假设您可以使用 = { string-literal } 对其进行初始化是不安全的。
  • @JohannesSchaub-litb。 AFAIK 'array' 是围绕 'char [N]' 的薄包装器,它本身可以用字符串文字初始化,因此对于 'array' 也可以使用字符串字面量。然而,尽管对我来说这似乎是合乎逻辑的,但最终还是要符合标准。如果人们被告知更喜欢 'std::array' 而不是内置 C 数组,我觉得支持字符串文字初始化很重要。
  • @JohannesSchaub-litb 你是对的,当然; 23.3.2.1p2 只要求 array 是一个聚合,它接受来自大括号括起来的初始化列表的复制初始化; T elemens[N] 成员仅用于说明,因此可以为 array&lt;char, N&gt; 设想一个不直接包含 char[N] 数组成员的类布局。
  • @ecatmur 该标准要求支持从可转换为T 的类型的初始化器列表进行初始化,而不仅仅是直接用于T。考虑struct S { operator int() { return 0; } template &lt;typename T&gt; operator T() = delete; };,然后考虑array&lt;int, 1&gt;{S()}。这会破坏您的实施。
  • @JohannesSchaub-litb 你断章取意,就标准而言,我的意思是无效。具体来说,为了使array 的实现有效,该实现需要专门支持此类构造。 :) 也就是说,我真的很喜欢array&lt;char, 1&gt;,因为它甚至不需要恶意实现来不包含数组作为其成员。
猜你喜欢
  • 2014-11-06
  • 2012-08-24
  • 2014-03-03
  • 2016-06-07
  • 1970-01-01
  • 2019-04-28
  • 1970-01-01
  • 2020-10-01
  • 1970-01-01
相关资源
最近更新 更多