将对象的所有权传递给同一对象的方法?

Hel*_*and 1 c++ ownership-semantics rvalue-reference unique-ptr c++14

我遇到了类似于以下(或多或少最小)示例的 C++ 代码。请考虑底部函数中标记的方法调用:

#include <memory>

static unsigned returnValue = 5;
void setReturnValue(unsigned u) { returnValue = u; }

class MyObject
{
    public:
        MyObject(unsigned uIN) : u(uIN) {}
        ~MyObject() { u = 42; }

        void method(std::unique_ptr<MyObject> uniqPtrToObject)
        {
            // Do something fancy with this unique pointer now, 
            // which will consume the object and its data
            setReturnValue(uniqPtrToObject->getValue());
        }
        unsigned getValue() { return u; }

    private:
        unsigned u;    // Some relevant object data
};

std::unique_ptr<MyObject> GetUniqToObject(unsigned u)
{
    // Get the object in a fancy way. For now, assume it has just been constructed.
    return std::make_unique<MyObject>(u);
}

int main()
{
    std::unique_ptr<MyObject> uniqPtrToObject = GetUniqToObject(0);

    // ===================================================
    // Oops!
    uniqPtrToObject->method(std::move(uniqPtrToObject));
    // ===================================================

    return returnValue;
}
Run Code Online (Sandbox Code Playgroud)

在调用对象的方法之一时移开对象看起来真的很奇怪。

我已经在https://godbolt.org ( )上尝试了我的示例x64-86 gcc 12.2,并且程序0按“应该”返回。只是一些幸运的巧合吗?

我仍然想知道:

  1. 这是有效的 C++ 语法吗?
  2. 如果是的话,它是便携式的吗?
  3. 如果是这样,这符合道德吗?

由于我还没有找到一个好的资源来开始研究这种情况,我希望您能提供一些建议,从哪里开始阅读或如何轻松地决定这种情况。

Nic*_*las 8

由于您将这个问题标记为 C++14,这意味着我们现在必须解决哪个表达式首先解析的问题:uniqPtrToObject->函数参数的一个还是初始化?答案是……不确定。在 C++14 中,两者都不在另一个之前排序。在 C++17 中,是->在函数调用及其参数计算之前排序的,但这是一个显式更改

这意味着在 C++14 中,代码的行为实际上是未定义的。如果参数的初始化unique_ptr发生在评估->表达式之前,则在评估时uniqPtrToObject将被移走。->因此,->获取 nullptr 并且您可以访问 nullptr 的状态。

现在,有一些评估顺序。但您不知道它是什么,编译器不需要告诉您,甚至不需要从一次执行到下一次执行保持一致。所以它是不可靠的。

假设我们正在谈论 C++17。这使得您的代码定义良好,因为它确保uniqPtrToObject->首先对其进行评估。

但是,即使您的代码“有效”,它在本示例的上下文中几乎肯定是无用的。原因是您uniqPtrToObject传递。这意味着method()销毁unique_ptr进而销毁它所拥有的对象。因此,如果对象在内部存储了一些数据,那么该数据现在就会丢失。该对象所做的任何修改都不再可访问,除非它存储在对象所有权范围之外的状态。

因此,即使在具有一致定义行为的 C++ 版本上,实际执行也不是一个好主意。

  • @RichardCritten 请参阅 OP 评论“您对这个近乎最小的示例的看法是正确的。在现实生活中,该方法有一些进一步的细节,说明为什么它不是静态的。您甚至可以想象它是虚拟的,并假设对象源中存在某种多态性(此处:获取UniqToObject()” (2认同)
  • @NicolBolas - 感谢您的有用回答(​​+1)。事实上,这个例子对于“method()”内部将做什么的细节是不完整的。我决定这样写,因为我希望收到我的简化=不完整问题的基本答案。我确实收到了 C++14 案例的完整答案 =&gt; 非常感谢。 (2认同)