【问题标题】:Why doesn't this create a dangling reference?为什么这不会创建一个悬空引用?
【发布时间】:2021-08-09 05:53:12
【问题描述】:

我认为 VS2019 的建议会产生悬空参考情况,但我对其进行了测试,它似乎有效。这里发生了什么?


    template<typename MessageType>
    class Queue {
      inline static std::vector<MessageType> messages;
    public:
      static bool isEmpty() {
        return messages.size() == 0;
      }

      template <typename... Args>
      static void emplace(Args&&... args) {
        messages.emplace_back(std::forward<Args>(args)...);
      }

      static MessageType pop() {
        auto const& val = messages.back();
        messages.pop_back();
        return val;
      }
    };

看起来最后一条消息的存活时间足够长,可以复制到返回值中。这是好的做法吗?

【问题讨论】:

  • 编译器没有义务找出编译代码中未定义行为的每个实例。每次它设法提醒您未定义的行为时,请将其视为额外的奖励。编译器在这里错过了这一点的事实仅仅意味着您不能总是期望您的编译器为您捕获所有错误。如果有就好了,但我们不能总是有好的东西......
  • VS 在这里是错误的:通过这些更改,这绝对是未定义的行为。
  • 您可以更改代码以避免与 warning 相关的 C++ 核心指南,方法是不使用 auto 作为类型:messageType val = messages.back();
  • auto const&amp; val = messages.back(); messages.pop_back(); return val; 是未定义行为,因为val 指的是在到达return 时已销毁的对象。但是auto const val = messages.back(); messages.pop_back(); return val; 完全没问题,因为val 是向量最后一个元素的副本,所以原件是否被破坏并不重要。
  • @alfC:在屏幕截图中,val 不是参考。在后面的代码中就是。

标签: c++ reference visual-studio-2019 dangling-pointer


【解决方案1】:

看起来最后一条消息的存活时间足够长,可以复制到返回值中。这是好的做法吗?

不幸的是,不,它没有,不,它不是。 std::vector&lt;T&gt;::back 的返回类型是一个左值引用。也许智能感知认为它是一个右值引用,在这种情况下,由于here 的规则,它的生命周期会延长。

但情况并非如此,这里的用法是未定义的行为。这是因为引用所指的列表中的项目已被破坏。它可能仍然有效的原因是因为该项目的内存仍然存在,因此可以正确读取。这只是运气(或不幸,如果您希望能够找到这些错误)。如果pop_back 销毁的项目拥有其他内存,那么您可能会看到不同的结果,例如 SEGFAULT。

【讨论】:

  • 我很好奇为什么当我弹出然后再次放置时我仍然得到预期值,这应该会覆盖最后一个值。
【解决方案2】:

我认为这个建议是正确的,并且是正确的,但前提是你以一种非常做作的方式解释它。

首先,它唯一告诉您的是.back() 返回一个引用并且它不需要 被分配。 当然,该引用可能稍后会悬空,实际上它在很大程度上取决于std::vector 的具体实现。 你不想这样。

要回答您的问题,是的,.pop_back() 行有时会创建一个悬空引用,具体取决于实现和运行时条件。正式地说,就是UB。

(来自https://en.cppreference.com/w/cpp/container/vector/pop_back

迭代器和对 last 元素的引用以及 end() 迭代器均无效。

其次,无论如何你都要返回一个副本,所以“某事”是可疑的,这与 IDE 的建议一致。 您不需要副本(IDE 也一样),但您也不需要参考。 您可能想要的是移动的副本,很有可能。

如果Message 可以移动并且通常应该是正确的并且IDE 不应该提出任何改进建议,那么这是有效的。 最坏的情况是复制,这是正确的行为。

      static MessageType pop() {
        auto val = std::move(messages.back());
        messages.pop_back();
        return val;
      }

总之,除此之外的任何 UB 或讨论都是您想要的最概念化的表达方式。


注意:我认为 auto valauto const val 更好,因为您随后允许 NRVO。我不是 100% 确定。


【讨论】:

  • 我喜欢你对 std::move 的建议,但我很确定 const 不会影响 NRVO/复制省略。
  • @ratiotile,是的,我不确定(正如我所说)。也许我误解了我最近看到的一次谈话。
猜你喜欢
  • 2011-11-21
  • 1970-01-01
  • 2019-05-28
  • 2021-12-18
  • 2020-01-18
  • 1970-01-01
  • 2022-01-20
  • 2018-12-20
  • 1970-01-01
相关资源
最近更新 更多