【问题标题】:This seems to be a bug in the clang++ compiler这似乎是 clang++ 编译器中的一个错误
【发布时间】:2014-02-21 14:06:40
【问题描述】:

以下代码正常执行(见here):

#include <iostream>

struct A {
    int i;
    A():i(1){}
    operator int&() { return i; }
};

int& rri = A();

int main()
{
    int& ri = A();
    std::cout << ri << '\n';
    std::cout << rri << '\n';
}

打印出来

1
1

正如预期的那样。但是,如果我注释掉 main() 中的前两个语句,只留下

std::cout << rri << '\n';

代码打印0

编辑:

假设问题出在我的代码上。但后来我问:标准中 8.5.3/5 中第三个要点的目的是什么,我将其复制如下:

对类型“cv1 T1”的引用由类型表达式初始化 “cv2 T2”如下:

  • 如果引用是左值引用和初始化表达式

    • 是一个左值(但不是位域),并且“cv1 T1”与“cv2 T2”是引用兼容的,或者

    • 有一个类类型(即T2是一个类类型),其中T1与T2没有引用相关,可以转换为类型的左值 “cv3 T3”,其中“cv1 T1”与“cv3 T3”引用兼容(通过枚举适用的转换函数 (13.3.1.6) 并选择最佳转换函数来选择此转换) 重载决议(13.3)),

    然后引用绑定到 第一种情况下的初始化表达式左值和左值 第二种情况下的转换结果(或者,在任何一种情况下,转换为 对象的相应基类子对象)。 [注:通常 左值到右值 (4.1)、数组到指针 (4.2) 和 不需要函数到指针(4.3)的标准转换,并且 因此被抑制,当这种直接绑定到左值时 完毕。 ——尾注]

【问题讨论】:

  • 经验法则:错误不在于编译器,而在于您对语言的理解。
  • 该程序未定义。
  • @CouchDeveloper 是的,这就是我的意思。我会编辑评论。
  • -1:这不是编译器中的错误。这是您代码中的错误。
  • 当您使用广泛使用的编译器(如 MS'、gcc、clang 等)时,我建议您始终认为问题出在您的代码中,而不是在编译器的。编译器错误非常少见(尤其是对于稳定版本),而且很可能 99.99% 的问题都出现在这里,这表明“编译器错误”似乎是代码中的错误。

标签: c++ c++11 reference clang++


【解决方案1】:

未定义行为:rirri 都被初始化为引用临时成员,该成员立即被销毁。

悬空引用指向一些可能被另一个对象重用的内存,在这种情况下,您可能会看到来自该对象的数据。或者您可能会看到其他类型的未定义行为。

【讨论】:

  • @CouchDeveloper 还是一样。仅当直接使用临时对象初始化引用时,才能延长临时对象的生命周期。在这里,它使用operator int&amp;() 的结果进行初始化,因此临时在表达式的末尾被销毁。
  • @WakeupBrazil:两者都被立即销毁。
  • @WakeupBrazil int&amp; ri = A(); 实际上是在说int&amp; ri = (int&amp;)A();。这实际上是对临时副本的引用。它不绑定到从A() 生成的对象,而是绑定到i 的内部引用。由于A() 的结果没有绑定到任何东西,所以它被销毁了,因此i 也随之销毁。
  • @WakeupBrazil:转换不一定会引用临时成员,因此行为不一定是未定义的(尽管在您给出的示例中)。
  • @WakeupBrazil 在他们在标准文档中给出的示例中,它没有指定 operator int&amp; 正在做什么。该部分的全部目的是向您展示如何初始化引用(而不是谈论它的生命周期)。虽然您的代码看起来很相似,但魔鬼在细节中。例如,如果 int i 改为 static int i,则不再是 UB。
猜你喜欢
  • 2018-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-17
  • 2017-08-31
  • 1970-01-01
  • 2017-05-22
相关资源
最近更新 更多