每周技巧 #107:引用生命周期延长
本节阅读量:本文翻译自 Abseil 官网的 Tip of the Week #107: Reference Lifetime Extension。
原文最初作为 totw/107 发布于 2015 年 12 月 10 日。
作者:Titus Winters (titus@google.com)
TotW 101 之后,有一些关于引用和生命周期的困惑反馈。因此在本技巧中,我们会更深入讨论这个问题:“什么时候会应用引用生命周期延长?”
|
|
简而言之,临时对象(被引用物)的生命周期会被延长,当且仅当:
- 一个局部
const T&(或T&&,尽管 Google 风格通常忽略这一点)被初始化为某个表达式的结果;该表达式通常是函数调用,并返回临时T,或者返回临时对象的T子对象(例如包含T的 struct)。
标准语言可能有点难拆解,所以我们讨论一些边界情况来澄清:
- 赋给
T&时不适用,必须是const T&。(否则是编译错误。) - 如果涉及(非多态)类型转换,则不适用。例如,从
string赋给const absl::string_view&不会延长string的生命周期。(你首先也不该使用const absl::string_view&,但那是另一个问题。) - 当你间接获取子对象时不适用:编译器不会看穿函数调用(getter 或类似函数)。子对象形式只在你直接从临时对象的 public 成员变量子对象赋值时有效。(所以这并不常见,因为我们很少有返回临时 struct 的表达式。)
- 允许类型转换的一种情况是:把
U的临时对象赋给T&,其中T是U的父类。请不要这样做:这比其他情况更让读者困惑。
如果临时对象的生命周期被延长,它会一直持续到引用离开作用域。如果临时对象的生命周期没有按上述方式延长,被引用的 T 会在语句结束时销毁(也就是遇到下一个 ; 时)。
按照 TotW 101 的建议,在显式引用初始化这种情况下,你大概不应该依赖生命周期延长:它并没有给你带来多少(或任何)性能收益,而且微妙、脆弱,容易给评审者和未来维护者增加额外工作。
有些微妙场景中,生命周期延长确实会发生,而且必要、有益(例如对临时容器做 ranged-for),但同样,延长的只是临时表达式结果的生命周期,不包括任何子表达式。例如,下面可以工作:
|
|
下面不能工作:
|
|
本节目录