简体   繁体   English

在哪些情况下(const)引用返回值是不安全的?

[英]In which cases is it unsafe to (const) reference a return value?

I tend do use the following notation a lot: 我倾向于使用以下符号:

const Datum& d = a.calc();
// continue to use d

This works when the result of calc is on the stack, see http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/ . 当calc的结果在堆栈上时,这可以工作,参见http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/ Even though compilers probably optimize here, it feels nice to explicitly avoid temporary objects. 即使编译器可能在这里优化,但明确避免临时对象感觉很好。

Today I realized, that the content of d became invalid after data was written to a member of a . 今天我意识到,内容d成为无效数据被写入的一员后a In this particular case the get function simply returned a reference to another member, which was completely unrelated to the write however. 在这种特殊情况下,get函数只是返回对另一个成员的引用,这与写入完全无关。 Like this: 像这样:

const Datum& d = a.get();
// ... some operation ...
a.somedata = Datum2();
// now d is invalid.

Again, somedata has nothing to do with d or get() here. 同样,somedata与dget()无关。

Now I ask myself: 现在我问自己:

  1. Which side-effects could lead to the invalidation? 哪些副作用可能导致失效?
  2. Is it bad-practice to assign return values to a const reference? 将返回值分配给const引用是不是很糟糕? (Especially when I don't know the interior of the function) (特别是当我不知道功能的内部)

My application is single-threaded except for a Qt GUI-Thread. 我的应用程序是单线程的,除了Qt GUI-Thread。

You seem to be afraid of elision failing. 你好像害怕失败。 That auto x = some_func(); 那个auto x = some_func(); will result in an extra move construct over auto&& x = some_func(); 将导致一个额外的移动构造超过auto&& x = some_func(); when some_func() returns a temporary object. some_func()返回一个临时对象时。

You shouldn't be. 你不应该。

If elision fails, it means your compiler is incompetent, or compiling with frankly hostile settings. 如果省略失败,则表示您的编译器无能力,或者使用坦率的恶意设置进行编译。 And you cannot survive hostile settings or incompetent compilers: incompetent compilers can turn a+=b with integers into for (int i = 0; i < abs(b); ++i) {if (b>0) ++a; else --a;} 并且你无法在恶意设置或不称职的编译器中存活:不称职的编译器可以将带有整数a+=b变成for (int i = 0; i < abs(b); ++i) {if (b>0) ++a; else --a;} for (int i = 0; i < abs(b); ++i) {if (b>0) ++a; else --a;} and violate not one iota of the standard. for (int i = 0; i < abs(b); ++i) {if (b>0) ++a; else --a;}并且违反标准的iota。

Elision is a core language feature. Elision是核心语言功能。 Don't write bad code just because you don't trust it will happen. 不要因为你不相信它会发生而编写坏代码。


You should capture by reference when you want a reference to the data provided by the function, and not an independently stable copy of the data. 当您想要引用函数提供的数据时,应该通过引用捕获,而不是单独稳定的数据副本。 If you don't understand the function's return value's lifetime, capturing by reference is simply not safe . 如果您不了解函数的返回值的生命周期,则通过引用捕获根本不安全

Even if you know that the data will be stable, more time is spent maintaining code than writing it: the person reading your code must be able to see at a glance that your assumption holds. 即使您知道数据将是稳定的,维护代码所花费的时间也比编写代码要多:阅读代码的人必须能够一目了然地看到您的假设成立。 And non-local bugs are bad: a seemingly innocuous change to the function you call should not break your code. 并且非本地错误很糟糕:对您调用的函数进行看似无害的更改不应该破坏您的代码。


The end result is, take things by value unless you have a good reason not to. 最终的结果是,除非你有充分的理由不去,否则按价值计算。

Taking things by value makes it easier for you, and the compiler, to reason about your code. 按值进行操作使您和编译器更容易推理您的代码。 It increases locality. 它增加了地方。

When you have a good reason not to, then take things by reference. 如果你有充分的理由不这样做,那么可以参考一下。

Depending on context, this good reason might not have to be very strong. 根据具体情况,这个好理由可能不一定非常强大。 But it shouldn't be based off an assumption of an incompetent compiler. 但它不应该基于一个无能的编译器的假设。

Premature pessimization should be avoided, but so should premature optimization. 应该避免过早的悲观,但应该过早优化。 Taking (or storing) references instead of values should be something you do when and if you have identified performance problems. 获取(或存储)引用而不是值应该是您在确定性能问题时所做的事情。 Write clean, easy to understand code. 编写干净,易于理解的代码。 Shove complexity into tightly written types, and have the exterior interface be clean and simple. 将复杂性推向紧密编写的类型,并使外部界面简洁明了。 Take things by value, because values decouple state. 按价值取物,因为价值观与国家脱钩。


Optimization is fungible. 优化是可替代的。 By making more of your code simpler, you can make it easier to work with (and be more productive). 通过使更多代码更简单,您可以更轻松地使用(并提高工作效率)。 Then, when you identify parts where performance matters, you can expend effort there to make the code faster. 然后,当你确定部分在性能的问题,你可以付出努力使代码更快。

A big example is foundational code: foundational code (which is used everywhere) quickly becomes a general drag on performance if not written with performance and ease of use in mind. 一个很好的例子是基础代码:基础代码(在任何地方使用)很快就会成为性能的一般拖累,如果不是考虑到性能易用性。 In that case, you want to hide complexity inside the type, and expose a simple easy to use exterior interface that doesn't require the user to understand the guts. 在这种情况下,您希望隐藏类型的复杂性,并展示一个简单易用的外部接口,不需要用户理解内部。

But code in a random function somewhere? 但代码在某个随机函数? Use values, the easiest to use containers with the friendliest O-notation to the most expensive operation you do and the easiest interface. 使用值,最容易使用容器和最友好的O符号来执行最昂贵的操作和最简单的界面。 Vectors if reasonable (avoid premature pessmiziation), but don't sweat a few maps. 矢量如果合理(避免过早的悲观),但不要冒出几张地图。

Find the 1%-10% of your code that takes up 90%-99% of the time, and make that fast. 找到占用90%-99%时间的代码的1%-10%,并快速完成。 If the rest of your code has good O-notation performance (so it won't become shockingly slower with larger data sets than you test with), you'll be in good shape. 如果你的代码的其余部分具有良好的O符号性能(因此,对于比你测试的更大的数据集,它不会变得惊人地慢),你将处于良好的状态。 Then start testing with ridiculous data-sets, and find the slow parts then. 然后开始用荒谬的数据集进行测试,然后找到慢速部分。

Which side-effects could lead to the invalidation? 哪些副作用可能导致失效?

Holding a reference to the internal state of a class (ie, versus extending the lifetime of a temporary) and then calling any non-const member functions may potentially invalidate the reference. 持有对类的内部状态的引用(即,相对于延长临时的生命周期),然后调用任何非const成员函数可能会使引用无效。

Is it bad-practice to assign return values to a const reference? 将返回值分配给const引用是不是很糟糕? (Especially when I don't know the interior of the function) (特别是当我不知道功能的内部)

I would say the bad practice is to hold a reference to the internal state of a class instance, mutating the class instance, and continuing to use the original reference (unless it is documented that the non-const functions don't invalidate the reference) 我会说不好的做法是保持对类实例的内部状态的引用,改变类实例,并继续使用原始引用(除非记录非const函数不会使引用无效)

I am not sure that I am answering exactly what you expected to hear but... const keyword has nothing to do with the 'unsafe' here. 我不确定我是否正在回答你期望听到的内容,但是...... const关键字与这里的“不安全”无关。 Even if you return non const reference, it may become invalid. 即使您返回非const引用,它也可能变为无效。 const means that it is not allowed to modify it. const表示不允许修改它。 For example if your get() returns a const member or get() itself is defined as const like this const some_refetence_t& get() const { return m_class_member; } 例如,如果你的get()返回一个const成员或者get()本身被定义为const就像这个const some_refetence_t& get() const { return m_class_member; } const some_refetence_t& get() const { return m_class_member; } . const some_refetence_t& get() const { return m_class_member; } Now regarding your questions at the end: 现在关于你的问题:

  1. Which side-effects could lead to the invalidation? 哪些副作用可能导致失效?

There could be many side effects if the original value changes. 如果原始值发生变化,可能会有许多副作用。 For example let's assume that the returned value is a reference to an object on the heap which is deleted... or the returned value is cached while the original value gets updates. 例如,假设返回的值是对堆上的对象的引用,该对象被删除...或者在原始值获得更新时缓存返回的值。 All these are design problems. 所有这些都是设计问题。 If by design such case can take place then the return value should be by value (and in case of caching, it must not be cached! :) ). 如果按照设计可以进行这样的情况,那么返回值应该是值(并且在缓存的情况下,它不能被缓存!:))。

  1. Is it bad-practice to assign return values to a const reference? 将返回值分配给const引用是不是很糟糕? (Especially when I don't know the interior of the function) (特别是当我不知道功能的内部)

Same thing. 一样。 If by design you don't have to modify the object that you get (by reference or by value) than it is the best to define it as const . 如果按照设计,您不必修改您获得的对象(通过引用或按值),而不是最好将其定义为const Once you define something as const , the compiler will make sure that you are not trying to modify it somehow in the code. 一旦你定义为const ,编译器将确保你不是试图在代码中以某种方式修改它。

It is rather question of knowing what your functions return. 这是一个知道你的函数返回的问题。 You should also have a full understanding of the return value type and it's semantics. 您还应该完全了解返回值类型及其语义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM