简体   繁体   中英

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

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.

I do not want to modify Foo or Bar and the compiler fails with 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?)

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.

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?

A compiler may do that when Foo is neither movable nor copyable.

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.

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. Pre-C++17, the "correct" way depends on context. 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. So you can return types that can be moved but not copied in the normal way without explicitly having to use 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. 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.

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. 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?

std::move doesn't actually move anything, it just changes a lvalue reference into a rvalue reference. 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.

* There is an exception to this rule for trivially copyable types for ABI compatibility (and potentially also performance) reasons.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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