std :: move,返回值优化和析构函数之间的交互

时间:2018-03-09 14:02:59

标签: c++11 copy-elision return-value-optimization stdmove

Stack Overflow上有许多关于std :: move和copy elision之间交互的线程,例如
What are copy elision and return value optimization?
How to be confident of copy elision / return-value optimization
c++11 Return value optimization or move?

然而,所有这些线程似乎都没有回答这个问题,是否可以 依赖 编译器进行必要的优化。

如果在最坏的情况下我们的性能略有下降,这可能是有问题的,但如果由于可能重复的析构函数调用而导致冲突正确,则可能会致命。

我有以下情况:

class ObjectContainer {

    Object* obj;

public:

    ObjectContainer(Object* o): obj(o) {}
    ~ObjectContainer() {
        if (obj) delete obj;
    }

    ObjectContainer(ObjectContainer&& other): obj(other.obj) {
        other.obj = nullptr;    
    }

};

ObjectContainer create_container() {
    return ObjectContainer(new Object());
}

那里会发生什么?这保证有效吗?或者是否存在代码无法编译的情况,或者更糟糕的是,析构函数在同一对象上被调用两次?

据我所知,如果我声明一个移动构造函数,编译器不会生成默认的复制构造函数。我是否必须指定复制构造函数?如果我这样做会怎么样?或者我应该在return语句中使用std :: move吗?

我打算像上面这样调用上面的方法:

void do_stuff() {
    do_some_other_stuff(create_container());
}

void do_some_other_stuff(ObjectContainer const& oc) {
    ...
}

1 个答案:

答案 0 :(得分:1)

  

那里会发生什么?这保证有效吗?或者是否存在代码无法编译的情况,或者更糟糕的是,析构函数在同一对象上被调用两次?

只要您使用自动变量和临时对象,C ++就会保证每个构造的对象都会被销毁。 Elisions不会破坏代码的含义;它只是一种优化。

复制elision并没有真正忽略副本/移动;它忽略了对象的存在。

在C ++之前的版本17中,您的代码表示要构造一个ObjectContainer临时,然后将其复制/移动到函数的ObjectContainer返回值中。编译器可以忽略临时的创建,而是直接从临时的给定参数构造返回值对象。这样做,它消除了复制/移动,但它也消除了它不会创建的临时性的破坏。

(在C ++ 17之后,这段代码说使用prvalue直接初始化返回值对象,因此这里不可能暂时使用它。)

  

我是否必须指定复制构造函数?

如果希望对象可以复制,则只需指定复制构造函数。如果为类型提供移动构造函数,编译器将自动= delete其他复制/移动构造函数/赋值运算符,除非您明确地编写它们。

  

如果我这样做会怎么样?

这一切都取决于你放入复制构造函数的内容。如果您对该值进行了标准副本,那么您已经破坏了类型的工作方式,因为它应该具有已分配指针的唯一所有权。

  

或者我应该在return语句中使用std :: move吗?

......你为什么这样?返回声明给出了一个prvalue; 从不需要在prvalues上使用std::move