如果编译器是未定义的行为,为什么会发出关于返回对本地堆栈变量的引用的警告?

Gob*_*0st 26 c++ compiler-construction standards

C++标准规定返回对局部变量(在堆栈上)的引用是未定义的行为,那么为什么许多(如果不是全部)当前编译器只会发出警告呢?

struct A{
};

A& foo()
{
    A a;
    return a; //gcc and VS2008 both give this a warning, but not a compiler error
}
Run Code Online (Sandbox Code Playgroud)

如果编译器为此代码提供错误而不是警告,那会不会更好?

允许这个代码只用警告编译有什么好处吗?

请注意,这不是一个const可以延长临时使用寿命的参考资料.

Dav*_*eas 23

从编译器的角度来看,验证是否返回对临时的引用几乎是不可能的.如果标准规定要将其诊断为错误,那么编写编译器几乎是不可能的.考虑:

bool not_so_random() { return true; }
int& foo( int x ) {
   static int s = 10;
   int *p = &s;
   if ( !not_so_random() ) {
      p = &x;
   }
   return *p;
}
Run Code Online (Sandbox Code Playgroud)

上述程序运行正确且安全,在我们当前的实现中,保证foo将返回对static变量的引用,这是安全的.但是从编译器的角度来看(并且使用单独的编译,not_so_random()无法访问实现,编译器无法知道程序是否格式正确.

这是一个玩具示例,但您可以想象具有不同返回路径的类似代码,其中p可能引用返回的所有路径中的不同长寿命对象*p.

  • 但是,在许多情况下,可以证明您的程序是这样做的,因此是错误的.事实上你不能总是发现错误是一个很好的理由,没有告诉用户他们犯了错误吗? (8认同)
  • +1:确实完全不可能检查你的例子(因为暂停问题是无法解决的). (3认同)

Ker*_* SB 10

未定义的行为不是编译错误,它不是一个格式良好的C++程序.并非每个不正常的程序都是不可编辑的,这是不可预测的.我敢打赌,原则上计算机无法确定给定的程序文本是否是格式良好的C++程序.

您始终可以添加-Werror到gcc以使警告终止编译并显示错误!

添加另一个最喜欢的SO主题:您是否也想++i++引起编译错误?

  • @JørgenFogh:标准允许:§1.3.12[defns.undefined]/1*[...] [注意:允许的未定义行为范围从[...]到**终止翻译**或执行(发布诊断信息)* (3认同)

Alo*_*ave 8

如果返回指向本地内部函数的指针/引用,只要不取消引用从函数返回的指针/引用,就可以很好地定义行为.

仅当退出指针时,它才是未定义的行为.

它是否是未定义的行为取决于调用函数的代码而不是函数本身.

因此,在编译函数时,编译器无法确定行为是未定义还是定义良好.它能做的最好的事情是警告你一个潜在的问题,它确实存在!

代码示例:

#include <iostream>

struct A
{ 
   int m_i;
   A():m_i(10)
   {

   } 
};  
A& foo() 
{     
    A a;
    a.m_i = 20;     
    return a; 
} 

int main()
{
   foo(); //This is not an Undefined Behavior, return value was never used.

   A ref = foo(); //Still not an Undefined Behavior, return value not yet used.

   std::cout<<ref.m_i; //Undefined Behavior, returned value is used.

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

参考C++标准:
第3.8节

在对象的生命周期开始之前但是在对象将占用的存储之后已经分配了34),或者在对象的生命周期结束之后并且在对象占用的存储被重用或释放之前,任何指针指的是可以使用对象将被定位或存在的存储位置,但仅限于有限的方式. 这样的指针指的是已分配的存储(3.7.3.2),并且使用指针就像指针一样type void*,是明确定义的.这样的指针可以被解除引用,但是所得到的左值可以仅以有限的方式使用,如下所述.如果对象将是或具有非平凡析构函数的类类型,并且指针用作delete-expression的操作数,则程序具有未定义的行为.如果对象将是或者是非POD类类型,则在以下情况下,程序具有未定义的行为:

- .......