编译器检测到返回对局部变量的引用

5

我刚刚遇到了一个非常不好的未定义行为,原因是返回了一个对本地变量的引用。

我们知道这很危险,通常编译器会打印一个漂亮的 warning 来提醒我们……但是 GCC(3.4.2)似乎没有把检查做得太严格。

std::string get_env_value(std::string const& key);

std::string const& get_phase()
{
  std::string const& phase = get_env_value("PHASE"); // [1]
  std::cout << "get_phase - " << phase << '\n';
  return phase;                                      // [2]
}

这段代码可以编译通过,但是我们却陷入了不确定行为的恶劣境地。

[1]行没问题,因为标准规定变量绑定到const引用的生命周期应该延长到const引用的生命周期。

[2]行似乎也没问题...

  • C++规范是否涵盖了这种情况?
  • 有人知道这通常是否被诊断出来吗?(我可能漏掉了某个标志之类的东西...)

我认为静态分析应该能够告诉我们,在使用“生命周期延长”时,[1][2]不安全,但我想这可能会很棘手...


1
最终可能会发生 get_env_value() 返回对一个变量的引用,该变量不会超出作用域,例如全局变量,在这种情况下一切都应该没问题。 - UncleBens
@UncleBens:这是一个很好的观点。 - Chubsdad
@UncleBens:get_env_value 返回的是一个副本,它怎么可能是全局作用域中变量的引用呢? - Matthieu M.
@Konrad:是的,相当恶心。我认为最糟糕的是,没有 get_env_value 签名,一切都看起来是正确的。 - Matthieu M.
2个回答

5
标准不涵盖[2]。它允许将rvalue绑定到const引用,但这并不能使您返回一个const引用并且rvalue的生命周期得到扩展。
当然,静态分析可能会捕获到这一点,但一如既往,这是个权衡。C++编译已经足够慢了,所以编译器编写者必须权衡进一步的静态分析带来的好处,这可能使他们能够产生更好的诊断结果,但代价是增加了编译时间。

可以设置lint来捕捉这个吗? - Steve Townsend
我同意编译速度够慢,编译器确实会警告缺少返回值和返回本地变量的引用,但我还是希望phase会引发警告 :/ - Matthieu M.

2
  1. 不,我认为标准并没有涉及/覆盖这种特定情况。

  2. VS 2010会给出编译警告(/Za,/W4)。

所以,显然这似乎是可诊断的情况。

因此,我稍微调整了函数,只是为了创建多个返回路径:

std::string const& get_phase() 
{ 
    std::string const& phase = get_env_value("PHASE"); // [1] 
    std::cout << "get_phase - " << phase << '\n'; 

    if(1){
        while(1){
            return phase;
        }
    }
    return phase;                                      // [2] 
} 

现在,VS不像以前一样报告警告。

例如,乍一看,编译器似乎应该很容易检测并捕获并非所有路径都返回值的情况。但是编译器(例如VS)并没有这样做。

int get_phase() 
{
    char ch;
    if(ch){
        return 0;
    }
     // nothing returned from here.
} 

因此,我猜测OP中的代码可能与上面示例中展示的具有相同的复杂性,但我不确定。唯一好的事情是标准对这种情况很明确。 $6.6.3/2 - "从函数流出等同于没有返回值的返回; 这会导致值返回函数的未定义行为。"
回到OP中的代码,我猜测标准并没有强制要求这个条件成为一个可诊断的条件,因此编译器可以自由地做任何操作。基本上可以理解为返回的引用指向一个已经被销毁的对象。因此访问这样的对象将会导致未定义行为。

@Chusbad:大多数编译器确实会警告没有返回值的函数(有时过于严格,但只在极端情况下)。我并没有真正期望出现错误...但是警告会非常好 :) - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接