【问题标题】:Why can't I initialize a reference in an initializer list with uniform initialization?为什么我不能用统一初始化初始化初始化列表中的引用?
【发布时间】:2012-05-17 14:03:03
【问题描述】:

也就是说,为什么会这样:

struct S {};

struct T
{
    T(S& s) : s{s} {}

    S& s;
};

int main()
{
    S s;
    T t{s};
}

给我一​​个 GCC 4.7 的编译器错误:

test.cpp: In constructor 'T::T(S&)':
test.cpp:5:18: error: invalid initialization of non-const reference of type 'S&' from an rvalue of type '<brace-enclosed initializer list>'

?

要修复错误,我必须将s{s} 更改为s(s)。这不会破坏统一初始化的,呃,uniformity

编辑:我尝试了 clang,并且 clang 接受了它,所以也许这是一个 GCC 错误?

【问题讨论】:

  • 在问 C++11 问题时,我会包括编译器和版本以防万一。并非所有编译器都支持所有功能,如果发现怪癖我不会感到惊讶
  • @DavidRodríguez-dribeas:好点,在编译器版本中添加
  • 您无法从值引用(初始化列表)初始化命名引用。如果您需要 c++ 标准,我可以为您指出 pdf 和页码。另外,需要注意的是,您的参考对象,也就是 &S,仅在赋值的右侧进行。不,它不会破坏统一初始化的统一性,初始化列表总是变量名(初始化)和逗号分隔。最后一个可验证的,没有逗号
  • @johnathon:我真的想要那个参考,因为我知道你可以做到。我添加了一个我认为合适的引号的答案。
  • @DavidRodríguez-dribeas 第 273 页.. 继续阅读。

标签: c++ reference c++11 initializer-list uniform-initialization


【解决方案1】:

我认为这是编译器中的错误。通过 list-initialization 处理引用初始化的两段是(在 n3337 中):

§8.5.4/3

类型 T 的对象或引用的列表初始化定义如下:

  • 否则,如果初始化列表有一个类型为 E 的元素,并且 T 不是引用类型或其引用类型与 E 引用相关,则从该元素初始化对象或引用;如果需要进行缩小转换(见下文)将元素转换为 T,则程序格式错误。

  • 否则,如果 T 是引用类型,则 T 引用的类型的纯右值临时是列表初始化的,并且引用绑定到该临时。 [注意:像往常一样,如果引用类型是对非常量类型的左值引用,则绑定将失败并且程序格式错误。 ——尾注]

编译器似乎在应用最后一段,而它应该应用第一段,因为 reference-related 被定义为

8.5.3/4

给定类型“cv1 T1”和“cv2 T2”,如果 T1 与 T2 的类型相同,或者 T1 是 T2 的基类,则“cv1 T1”与“cv2 T2”引用相关。

问题的情况下,大括号初始化列表里面的引用和初始化器的类型是完全一样的,也就是说初始化应该是有效的。


在 FDIS 草案中,等效段落的顺序颠倒了。这意味着 FDIS 草案 (n3290) 不允许 *lvalue*s 的 brace-list-initialization。另一方面,阅读文本似乎很明显这是标准中的一个错误,并且意图具有n3337的顺序:

  • 否则,如果 T 是引用类型,则 T 引用的类型的纯右值临时被列表初始化,并且引用绑定到该临时。

  • 否则,如果初始化列表只有一个元素,则从该元素初始化对象或引用;如果需要进行缩小转换(见下文)将元素转换为 T,则程序格式错误。

该文档中的顺序意味着由于所有引用类型都由第一个子句处理,因此在以下段落中提及 reference 是没有意义的。

【讨论】:

    【解决方案2】:

    是的,它是bug。这是新事物,并在 2012 年 2 月的工作文件中投票 (link)。

    Nicol Bolas 指出 gcc 实际上是符合 FDIS 批准的 C++11 标准的编译器,因为在那之后对工作文件进行了更改。

    【讨论】:

    • 请先生,告诉我在那个工作文件中,在那个链接中提到的那个部分,你可以将一个带括号的 initlizier 列表绑定到一个参考?
    • 或者,它说大括号 initlizer 列表可以在构造器实现的 inlitlizer 列表中使用
    • @johnathon:链接是 DR 1288,它描述了所做的更改(由 David Rodríguez - dribeas 引用)。在您的评论中,您还提供了标准中的答案in a mem-initializer (12.6.2)
    • C++11 之所以如此命名,是因为它于 2011 年获得批准。之后所做的更改即使是缺陷也不适用。因此,如果它不在实际的 C++11 标准中,那么编译器应该 拒绝它。这样做不是错误。 接受它是一个错误。
    • @NicolBolas:我不确定你所说的是否反映了编译器社区的共识。编译器会定期对缺陷报告进行修复,并将其视为标准的一部分。毕竟,这就是标准委员会将变更标记为缺陷报告的意思——标准中应该有的东西。
    【解决方案3】:

    (注意:我写这个答案是在最初的问题之后两年的事后发现的;并将来自 cmets 的一些信息放入一个实际的答案中,以便它可以搜索)。


    当然,使用S&amp; 类型的引用初始化S&amp; 类型的引用应该是直接绑定的。

    问题是 C++11 标准中的一个缺陷DR1288 已解决。更正后的文本出现在 C++14 中。

    委员会已经澄清,更正后的文本是为 C++11 设计的,因此“符合标准的编译器”应该实现更正后的版本。

    g++ 4.8 遵循 C++11 标准的已发布文本;然而,一旦这个问题曝光,g++ 4.9 实现了更正的版本,即使使用-std=c++11 开关也是如此。

    请注意,问题不仅限于构造函数初始化列表,例如:S s; S &amp;t{s}; 在 g++ 4.8 中不起作用,S s; S &amp;t = s; S &amp;u { t }; 也不起作用

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-13
      • 2014-08-23
      • 1970-01-01
      • 2018-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多