【问题标题】:Is it safe to call a method on a smart pointer that is moved-from in the arguments?在参数中移动的智能指针上调用方法是否安全?
【发布时间】:2020-06-27 06:12:10
【问题描述】:

这段代码安全吗?如果方法接受值或右值引用,答案会改变吗? unique_ptr 有变化吗?

struct foo
{
    void bar(std::shared_ptr<foo> p) // or std::shared_ptr<foo>&&
    {
        // the object will be deleted at the end of the call unless p is
        // moved/copied elsewhere at some point
    }
};

int main()
{
    auto p = std::make_shared<foo>();
    p->bar(std::move(p));
    return 0;
}

主要问题具体在这一行:

p->bar(std::move(p));

这是否保证在构造参数之前始终捕获p.operator-&gt;() 的当前值?还是在从p 移出后会发生这种情况?

(注意:我相信这对于std::shared_ptr&lt;foo&gt;&amp;&amp; 参数是安全的,因为如果有的话,实际的移动构造直到在方法体内部才会发生。但是当按值传递时,参数构造可以[包括移动构造] 在调用 p.operator-&gt;() 之前发生,还是总是严格在之后?)

【问题讨论】:

  • 这是一个关于特定的、旧的、C++ std 还是关于所有从不早于 C++11 开始的 C++ std 版本的 Q?
  • 我引用 C++11 作为最低要求,因为那是 unique_ptrshared_ptr 被引入的时候。我实际上混合了 C++11 和 C++17 应用程序,而 walnut 的回答解决了这两个问题。

标签: c++ c++11 language-lawyer shared-ptr move-semantics


【解决方案1】:

如果您使用std::shared_ptr&lt;foo&gt;&amp;&amp; 作为参数类型(或任何引用类型),那么没有问题,因为std::move 调用实际上并没有以任何方式修改对象。先执行没关系。

在 C++17 之前,按值变体是不安全的。没有排序规则可以保证在函数参数初始化之前计算命名函数的表达式。所以函数参数的移动构造可能发生在p-&gt;bar被评估之前。

自 C++17 起,命名函数的后缀表达式的值计算和所有副作用在调用参数中所有表达式的值计算和副作用之前排序,这意味着函数的构造函数在评估p-&gt;bar 之后对参数进行排序。见[expr.call]/5。 因此,p 上的潜在问题 operator-&gt; 调用首先发生,在移动构造之前,使代码安全。

如果您将std::shared_ptr 替换为std::unique_ptr,则同样的语句成立。

【讨论】:

  • 是的,我说了第一部分,但其余部分回答了我的问题。附近的另一个有趣的阅读是下一条规则,它指出虚拟方法是协变的,这是我以前不知道的非常有用的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-14
  • 2015-01-24
  • 2019-11-22
  • 1970-01-01
  • 1970-01-01
  • 2020-03-16
  • 2020-02-03
相关资源
最近更新 更多