std::move、return 值优化和析构函数之间的交互
Interaction between std::move, return value optimization and destructors
Stack Overflow 上有很多关于 std::move 和复制省略之间的交互的线程,例如
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) {
...
}
What will happen there? Is this guaranteed to work? Or can there be a situation in which the code doesn't compile or, worse, the destructor gets called twice on the same object?
只要您使用自动变量和临时对象,C++ 就保证每个构造的对象都会被销毁。省略不会破坏代码的含义;这只是一个优化。
复制省略并没有真正省略 copy/move;它忽略了 object.
的存在
在 pre-C++17 中,您的代码说要构造一个 ObjectContainer
临时对象,然后 copy/move 将其 ObjectContainer
return 值从功能。编译器可能会省略临时对象的创建,而是直接从临时对象的给定参数构造 return 值对象。这样做,它消除了 copy/move,但它也消除了它不创建的临时文件的破坏。
(在post-C++17中,这段代码说直接使用纯右值初始化return值对象,所以这里不可能使用临时值。)
Do I have to specify a copy constructor?
如果你想让对象可复制,你只需要指定一个复制构造函数。如果您为类型提供移动构造函数,编译器将自动 = delete
其他 copy/move constructors/assignment 运算符,除非您显式编写它们。
What happens if I do?
这完全取决于您放入复制构造函数中的内容。如果您对值进行标准复制,那么您就破坏了您的类型的工作方式,因为它应该拥有分配指针的唯一所有权。
Or should I use std::move in the return statement?
...为什么会这样? return 语句被赋予一个纯右值;你永远需要对纯右值使用std::move
。
Stack Overflow 上有很多关于 std::move 和复制省略之间的交互的线程,例如
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) {
...
}
What will happen there? Is this guaranteed to work? Or can there be a situation in which the code doesn't compile or, worse, the destructor gets called twice on the same object?
只要您使用自动变量和临时对象,C++ 就保证每个构造的对象都会被销毁。省略不会破坏代码的含义;这只是一个优化。
复制省略并没有真正省略 copy/move;它忽略了 object.
的存在在 pre-C++17 中,您的代码说要构造一个 ObjectContainer
临时对象,然后 copy/move 将其 ObjectContainer
return 值从功能。编译器可能会省略临时对象的创建,而是直接从临时对象的给定参数构造 return 值对象。这样做,它消除了 copy/move,但它也消除了它不创建的临时文件的破坏。
(在post-C++17中,这段代码说直接使用纯右值初始化return值对象,所以这里不可能使用临时值。)
Do I have to specify a copy constructor?
如果你想让对象可复制,你只需要指定一个复制构造函数。如果您为类型提供移动构造函数,编译器将自动 = delete
其他 copy/move constructors/assignment 运算符,除非您显式编写它们。
What happens if I do?
这完全取决于您放入复制构造函数中的内容。如果您对值进行标准复制,那么您就破坏了您的类型的工作方式,因为它应该拥有分配指针的唯一所有权。
Or should I use std::move in the return statement?
...为什么会这样? return 语句被赋予一个纯右值;你永远需要对纯右值使用std::move
。