【问题标题】:Safe to return const & to object passed by const &?安全地将 const & 返回到 const & 传递的对象?
【发布时间】:2015-08-11 16:32:42
【问题描述】:

使用 clang-3.5 将以下代码编译为 C++11 标准时:

const String& XMLAttributes::getValueAsString(const String& attrName, const String& def) const
{
    return (exists(attrName)) ? getValue(attrName) : def;
}

我收到以下警告:

warning: 
  returning reference to local temporary object [-Wreturn-stack-address]
    return (exists(attrName)) ? getValue(attrName) : def;
                                                     ^~~
note: 
  binding reference variable 'def' here
  ...String& attrName, const String& def) const
                                 ^
1 warning generated.

g++-4.9 没有给出类似的警告。

我认为 clang 过于热心并且在这种情况下它应该可以正常工作,因为我知道当使用这个函数时,输入有足够长的生命周期。 (我很确定我已经看到了大量似乎以这种方式工作的代码......)

不过,clang 说它是“本地临时对象”这一事实让我有点害怕。真的没有应该有一个本地临时对象,如果clang这么认为并且在这个函数运行时正在删除东西,我想知道为什么。

标准是否保证此函数返回的引用(在选择“def”的情况下)与传入的引用具有相同的生命周期,或者是否允许将它们视为两个不同生命周期的不同引用?

【问题讨论】:

  • 它有签名const String& XMLAttributes::getValue(const String& attrName) const 所以我想这不是问题

标签: c++ c++11 pass-by-reference


【解决方案1】:

不,这不安全。

在这种情况下,三元条件运算符计算为其操作数的副本。因此发出警告。

您可以通过注入一个漂亮的小std::ref 来解决它。

[C++11: 5.16/6]:第二个和第三个操作数的类型相同;结果就是那种类型。如果操作数具有类类型,则结果是结果类型的临时纯右值,根据第一个操作数的值从第二个操作数或第三个操作数复制初始化。

【讨论】:

  • 如果getValue() 返回const String& 则它不会评估为副本:[expr.cond]/4 "如果第二个和第三个操作数是相同值类别的全局变量并且具有相同的值类型,结果就是那个类型和值的类别”。
  • @MattMcNabb:好吧,但是由于警告 确实 生成了,我认为这里可以安全地假设情况并非如此。不过,我本来可以更清楚。
  • @MattMcNabb 是否可以用 if/else 替换三元条件运算符来解决问题,或者这不会改变什么?将三元运算结果分配给 const String& var 并返回该 var 怎么样?
  • @Ident 我不清楚问题到底是什么,因为 OP 没有发布MCVE。正如我在回答中所解释的那样,我们无法从这个 sn-p 判断它是否是虚假警告,或者它是否真正指出错误,因为 OP 错误地调用了函数
  • @LightnessRacesinOrbit 也许您忽略了 OP 在 cmets 中的说明,即 getValue() 确实返回 const String&? (当然他也有可能搞错了)
【解决方案2】:

def 不是这里的临时对象。如果getValue 实际返回const String& 则条件运算符也不会生成临时对象。

但是这种功能设计很危险,因为没有针对此类错误的保护:

const String &bar = getValueAsString( "bla", "" );   // oops

getValueAsString 返回时,从"" 创建的临时字符串消失,所以bar 现在是一个悬空引用(当然,如果触发了默认值)。

仅当临时对象直接绑定到引用时才会发生生命周期延长。从该引用初始化其他引用不会重新延长临时的生命周期。

可能 clang 实际上检测到您的代码确实包含此错误,即您在调用此函数时将临时绑定到 def

【讨论】:

  • 您给出的示例特别相关,因为声明中的默认值为第二个参数的“”,就像在您的示例中一样。 Clang 似乎也是我们发现的唯一检测到这一点的编译器。 Visual Studio 2008-2013 肯定也不行。作为旁注:我们的库被用于许多应用程序的生产代码中,奇怪的是,似乎没有人遇到这个问题或收到警告——或者至少没有人报告问题。我们为下一个主要版本修复了它。
  • @Ident 很酷,很高兴我能提供帮助。此错误的影响可能表现为间歇性的怪异行为(“Heisenbug”),很难提交错误报告。
猜你喜欢
  • 1970-01-01
  • 2013-10-06
  • 1970-01-01
  • 2012-11-07
  • 2018-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-21
相关资源
最近更新 更多