在参数中移出的智能指针上调用方法是否安全?

Is it safe to call a method on a smart pointer that is moved-from in the arguments?

这段代码安全吗?如果方法接受值或右值引用,答案会改变吗? 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->() 的当前值?或者这会在从 p 移动后发生吗?

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

如果您使用 std::shared_ptr<foo>&& 作为参数类型(或任何引用类型),则没有问题,因为 std::move 调用实际上不会以任何方式修改对象。先不执行都无所谓

在C++17之前,按值变体是不安全的。没有任何顺序规则可以保证命名函数的表达式在函数参数初始化之前被求值。因此函数参数的移动构造可能发生在 p->bar 被评估之前。

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

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