在哪些情况下(const)引用返回值是不安全的?
In which cases is it unsafe to (const) reference a return value?
我经常使用以下符号:
const Datum& d = a.calc();
// continue to use d
当calc的结果在堆栈上时,此操作有效,参见http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/。尽管编译器可能会在这里进行优化,但显式地避免临时对象感觉很好。
今天我意识到,当数据写入a
的一个成员后,d
的内容失效了。在这种特殊情况下,get函数只是返回对另一个成员的引用,但这与写操作完全无关。这样的:
const Datum& d = a.get();
// ... some operation ...
a.somedata = Datum2();
// now d is invalid.
同样,这里的一些数据与d
或get()
无关。
现在我问自己:
- 哪些副作用会导致无效?
- 将返回值赋给const引用是不好的做法吗?(特别是当我不知道函数的内部)
我的应用程序是单线程的,除了一个Qt gui线程。
你似乎害怕省略失败。当some_func()
返回一个临时对象时,auto x = some_func();
会在auto&& x = some_func();
之上产生一个额外的move构造。
你不应该。
如果省略失败,这意味着你的编译器是不称职的,或者是在非常不友好的设置下编译。而且您无法在恶意设置或不合格的编译器中生存:不合格的编译器可以将带整数的a+=b
转换为for (int i = 0; i < abs(b); ++i) {if (b>0) ++a; else --a;}
,并且丝毫不违反标准。
当您需要对函数提供的数据的引用而不是数据的独立稳定副本时,您应该通过引用捕获。如果你不了解函数的返回值的生命周期,那么通过引用进行捕获就是不安全的。
即使你知道数据是稳定的,花在维护代码上的时间比写代码的时间要多:读你代码的人必须一眼就能看出你的假设是成立的。非局部bug是不好的:对你调用的函数的一个看似无害的改变不应该破坏你的代码。
最终的结果是,除非你有很好的理由不这样做,否则按值取值。
按值取值使你和编译器更容易对你的代码进行推理。它增加了局部性
当你有很好的理由不这样做时,就参考一下。
根据上下文,这个好的理由可能不需要非常有力。但是它不应该基于一个不称职的编译器的假设。
应避免过早悲观,但也应过早优化。当您发现了性能问题时,应该使用(或存储)引用而不是值。编写简洁易懂的代码。将复杂性塞进紧密编写的类型中,并使外部接口干净而简单。按值取东西,因为值可以解耦状态。
优化是可替代的。通过简化更多的代码,您可以使其更容易使用(并且更高效)。然后,当您确定性能重要的部分时,您可以在上花费精力使代码更快。一个典型的例子是基础代码:如果在编写基础代码时没有考虑到性能和的易用性,那么基础代码(到处都在使用)很快就会拖累性能。在这种情况下,您希望将复杂性隐藏在类型中,并公开一个简单易用的外部接口,该接口不需要用户理解其内部结构。
但是代码在一个随机的函数某处?使用值,这是最容易使用的容器,具有最友好的o符号,对最昂贵的操作和最简单的接口。载体是合理的(避免过早的呼吸),但不要担心一些地图。
找出1%-10%的代码占用了90%-99%的时间,并使其快速。如果代码的其余部分具有良好的o符号性能(因此在使用比您测试的数据集更大的数据集时,它不会变得惊人地慢),那么您将处于良好状态。然后开始用荒谬的数据集测试,然后找到慢的部分。
哪些副作用会导致无效?
持有类的内部状态的引用(即,与扩展临时对象的生命周期相比),然后调用任何非const成员函数可能会使引用失效。
将返回值赋给const引用是不好的做法吗?(特别是当我不知道函数的内部时)
我会说坏做法是保持对类实例的内部状态的引用,改变类实例,并继续使用原始引用(除非文档中说明非const函数不会使引用无效)
我不确定我的回答完全是您期望听到的,但是…const
关键字与这里的"不安全"无关。即使返回非const引用,它也可能无效。const
表示不允许修改。例如,如果get()
返回const成员,或者get()
本身被定义为const
,就像const some_refetence_t& get() const { return m_class_member; }
一样。现在关于你最后的问题:
-
哪些副作用会导致无效?
如果原始值发生变化,可能会产生许多副作用。例如,让我们假设返回值是对堆上已删除的对象的引用…或者在更新原始值的同时缓存返回值。所有这些都是设计问题。如果按照设计这种情况可以发生,那么返回值应该是按值返回的(并且在缓存的情况下,它不能被缓存!):)) .
-
将返回值赋给const引用是不好的做法吗?(特别是当我不知道函数的内部时)
一样。如果按照设计,您不必修改获得的对象(通过引用或通过值),那么最好将其定义为const
。一旦您将某些内容定义为const
,编译器将确保您不会试图在代码中以某种方式修改它。
这是知道你的函数返回什么的问题。您还应该完全理解返回值类型及其语义。
- 为什么模板类中的对象不能返回值
- 编译器警告:执行到达值返回函数的末尾而不返回值
- 在 Arduino 上使用 sscanf 会导致与 const char * 不匹配,并且返回值始终相同,尽管输入值不同
- 方法错误"not all control paths return a value"和方法不返回值
- 程序不向函数返回值
- C++ 特征图3.5,特征图不使用命名返回值优化?
- 显示C++输出而不返回值
- 如何在不使用临时变量的情况下取消引用返回指针的函数的返回值?
- 从 C++ 函数与 Python 函数返回的不一致值用于偏斜正态分布
- 为什么当你将函数的返回值乘以零时它不会短路?
- 难以理解为什么返回值不会更新值
- 为什么函数的返回值不是符号名称的一部分
- 返回值不变的函数
- 绑定的返回值不是int
- 使用c++数学比较函数返回值不起作用
- 函数的返回值不能被识别为左值
- 标准库中自赋值不安全的move赋值操作符的基本原理是什么?
- 在哪些情况下(const)引用返回值是不安全的?
- 可以声明一个c++函数,使其返回值不能被忽略吗?
- 动态多态内存容器-返回值不正确