简体   繁体   English

在unique_ptr中包装一个没有移动构造函数的C++对象?

[英]Wrap a C++ object without move constructor in a unique_ptr?

So I'm pretty new to move-semantics and modern C++ in general, but as far as I understood, I could write code like所以总的来说,我对移动语义和现代 C++ 还很陌生,但据我所知,我可以编写如下代码

Foo doSomething(Bar b) {
    Foo f{b};
    return f;
}

Note that I did not explicitly write std::move to allow the compiler to apply RVO.请注意,我没有明确编写 std::move 来允许编译器应用 RVO。

I do not want to modify Foo or Bar and the compiler fails with Call to implicitly-deleted copy constructor of 'Foo' .我不想修改 Foo 或 Bar 并且编译器因Call to implicitly-deleted copy constructor of 'Foo'失败。

So I tried to fix this by introducing a move:所以我试图通过引入一个动作来解决这个问题:

Foo doSomething(Bar b) {
    Foo f{b};
    return std::move(f);
}

Which fails with the same error.哪个失败并出现相同的错误。 (Bonus question: why does the compiler complain about a copy constructor when I want to move the Foo?) (额外问题:当我想移动 Foo 时,为什么编译器会抱怨复制构造函数?)

So my idea is to wrap Foo in a std::unique_ptr and my question is whether this is the "correct" way to fix it.所以我的想法是将 Foo 包装在std::unique_ptr ,我的问题是这是否是修复它的“正确”方法。

std::unique_ptr<Foo> doSomething(Bar b) {
    auto f = std::make_unique<Foo>(b);
    return f;
}

Bonus question: why does the compiler complain about a copy constructor when I want to move the Foo?额外问题:当我想移动 Foo 时,为什么编译器会抱怨复制构造函数?

A compiler may do that when Foo is neither movable nor copyable.当 Foo 既不可移动也不可复制时,编译器可能会这样做。

So my idea is to wrap Foo in a std::unique_ptr and my question is whether this is the "correct" way to fix it.所以我的想法是将 Foo 包装在 std::unique_ptr 中,我的问题是这是否是修复它的“正确”方法。

You should probably return a prvalue:您可能应该返回一个纯右值:

Foo doSomething(Bar b) {
    return Foo{b};
}

Note that this requires C++17 to work if Foo cannot be made movable.请注意,如果 Foo 不能移动,这需要 C++17 才能工作。 Pre-C++17, the "correct" way depends on context. Pre-C++17,“正确”的方式取决于上下文。 Unique pointer might be OK, or alternatively not writing the function in the first place.唯一指针可能没问题,或者不首先编写函数。

As I understand it.据我了解。

Traditionally return value optimization was just that, an optimization (albeit one that could potentially change the behavior of your program).传统上返回值优化就是这样,一种优化(尽管可能会改变程序的行为)。 So the copy constructor had to be available (not be "deleted"), even if the compiler decided in the end that it did not need to use it.因此复制构造函数必须可用(而不是“删除”),即使编译器最终决定不需要使用它。

C++11 introduced move semantics, and part of that was a special rule that allowed move constructors to be used when returning a value. C++11 引入了移动语义,其中一部分是允许在返回值时使用移动构造函数的特殊规则。 So you can return types that can be moved but not copied in the normal way without explicitly having to use std::move.因此,您可以返回可以移动但不能以正常方式复制的类型,而不必显式使用 std::move。

C++17 changed the rules for anonymous return values such that they were guaranteed to be copy-free* and no-longer required a copy or move constructor. C++17 更改了匿名返回值的规则,以保证它们是无复制的*,并且不再需要复制或移动构造函数。 However it did not tackle the more complex issue of named return values.然而,它没有解决更复杂的命名返回值问题。

There is a proposal to tackle the issue of named return values ( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2025r0.html ), but I have no idea if it will be accepted.有一个解决命名返回值问题的建议( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2025r0.html ),但我不知道它是否会被接受。

Wrapping in a unique_ptr is indeed a way to effectively turn an immovable object into a movable one, but it also means the object is stored on the heap instead of the stack.包装在 unique_ptr 中确实是一种有效地将不可移动对象转换为可移动对象的方法,但这也意味着该对象存储在堆中而不是堆栈中。 There are pros and cons to storing objects on the heap, on the one hand there is generally more heap space than stack space available on most systems, on the other hand allocating and deallocating memory on the heap is more expensive than on the stack and the extra level of indirection has some cost too.在堆上存储对象有利有弊,一方面,在大多数系统上,堆空间通常比可用的堆栈空间更多,另一方面,在堆上分配和释放内存比在堆栈上更昂贵,并且额外的间接级别也有一些成本。

Bonus question: why does the compiler complain about a copy constructor when I want to move the Foo?额外问题:当我想移动 Foo 时,为什么编译器会抱怨复制构造函数?

std::move doesn't actually move anything, it just changes a lvalue reference into a rvalue reference. std::move 实际上并没有移动任何东西,它只是将左值引用更改为右值引用。 If a move constructor exists then std::move will cause it to be used in favor of the copy constructor, but std::move will not stop the copy constructor being used if no move constructor exists.如果存在移动构造函数,则 std::move 将使其被用于支持复制构造函数,但如果不存在移动构造函数,则 std::move 不会停止使用复制构造函数。

* There is an exception to this rule for trivially copyable types for ABI compatibility (and potentially also performance) reasons. * 出于 ABI 兼容性(以及潜在的性能)原因,对于可简单复制的类型,此规则有一个例外。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM