You are looking for copy-elision.
In short, your code is guaranteed to work in C++17, as described at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html
In C++14 and before there is no such guarantee.
Copy elision is an optimization that existed before C++11 and allowed compiler to omit copy constructor call in some cases.
C++11 has added move semantics and copy elision was expanded to allow compiler to avoid moving or copying (if moving is not available) an object.
Regardless of what compiler actually does, your class must still provide copy or move constructor, even if one will not be used by the compiler.
C++17 introduces "guaranteed copy-elision", which allows you to return objects of non-movable classes, just like in your case. Note that proposal explicitly mentions "factory functions". Quote:
it is impossible or very difficult to write factory functions
Example from proposal has this example:
struct NonMoveable {
NonMoveable(int);
NonMoveable(NonMoveable&) = delete;
void NonMoveable(NonMoveable&) = delete;
std::array<int, 1024> arr;
};
NonMoveable make() {
return NonMoveable(42); // ok, directly constructs returned object
}
As of today, both Clang and GCC are able to compile that code with -std=c++17
flag, but not with -std=c++14
.
I see two ways for you to solve this problem:
- Use C++17: I recommend deleting both copy and move constructor (and
operator=
) to make sure your code will not compile under earlier standard with buggy effects.
- Rely only on things that are available in C++14 to make your code work everywhere. You might want add additional state to your object, delete copy constructor and implement move constructor.
This is an example of how it can be done in C++14.
class Foo {
public:
Foo() = default;
Foo(const Foo &) = delete;
Foo(Foo &&rvalue) noexcept { std::swap(blocked, rvalue.blocked); }
~Foo() { if (blocked) unblock();
void block() { blocked = true; }
void unblock() { blocked = false; }
private:
bool blocked{false};
};