如何正确地从函数返回中移动对象?

Posted

技术标签:

【中文标题】如何正确地从函数返回中移动对象?【英文标题】:How to move object from a function return correctly? 【发布时间】:2020-04-24 10:50:18 【问题描述】:

我有一个不应被复制而只能移动的对象,因此我试图确保函数返回对象的T&&。我的问题是对象在返回之前就被销毁了,我不知道如何正确地做到这一点。

#include <iostream>

class Foo
public:
    Foo()  
        a = new int; 
        std::cout << a << '\n'; 
    
    Foo(const Foo& other) = delete;
    Foo(Foo&& other)  
        a = other.a; other.a = nullptr; 
    
    ~Foo()  
        std::cout << a << '\n';
        delete a; 
    
    void operator= (const Foo& other) = delete;
    void operator= (Foo&& other)  
        a = other.a; 
        other.a = nullptr; 
    
    int* a;
;

Foo&& createNewFoo()

    return std::move(Foo());


int main()

    Foo foo = createNewFoo(); // The pointer is deleted before this assigns to foo


【问题讨论】:

顺便说一句。 void operator=() 看起来有点奇怪。我习惯了返回类型引用(即return *this;)。 Canonical implementations - Assignment operator 继续 Scheff 所说的话。 void operator=() 是合法且允许的,但它是非惯用的,这增加了使用该对象的任何其他开发人员的摩擦。因为它会以令人惊讶和不寻常的方式表现。这是一个建议,不是规则以通常的方式执行,可能有充分的理由进行非典型实施。 【参考方案1】:

您正在构造一个临时对象并将其绑定到右值引用作为返回值,该临时对象将在完整表达式后立即销毁,然后返回的引用始终悬空。

将函数更改为按值返回就可以了;右值将被移动到返回值。

Foo createNewFoo()

    return Foo();

顺便说一句:对于上面的代码,Foo foo = createNewFoo(); 不会复制/移动任何东西,因为copy elision,这是从 C++17 开始保证的。

编辑

那么为什么在返回return Foo();时临时不立即销毁

按值返回,返回顺序为

    临时搭建 从临时移动构造返回值 临时被销毁

对于按引用返回,顺序是

    临时搭建 将临时值绑定到返回值 临时被销毁 返回值(引用)变得悬空

【讨论】:

那么为什么在返回时临时没有立即销毁 return Foo();引用是否在分配之前被销毁,而返回对象本身意味着在之后销毁它? 我明白了。当您在这些步骤中说“返回值”时,您的意思是在将函数分配给 foo 之前从函数创建的临时值 Foo foo = createNewFoo();对?所以本质上,假设没有优化,按值返回,有一个从本地临时到返回值的移动,然后在 Foo foo = createNewFoo(); ? @Zebrafish 是的,有 2 个动作(在 C++17 之前的概念中)。 (事实上​​两者都被省略了。)【参考方案2】:

您只需执行以下操作:

static Foo createNewFoo() 
    return Foo();

由于Foo() 已经是一个右值,因此不需要std::move

【讨论】:

Foo() 不是右值引用!它根本不是参考。 @IgorR。对不起,我想快点写错了。答案已更新

以上是关于如何正确地从函数返回中移动对象?的主要内容,如果未能解决你的问题,请参考以下文章

如何有效地从 Pandas 数据框移动到 JSON

OpenGL - 如何跟踪移动的物体? [关闭]

Unity 中的物理对象在低速时无法正确反弹

你如何正确地从 Promise 返回多个值?

QPainter如何传输? (从工厂函数中移动一个对象)

移动硬盘函数不正确要如何寻回资料