When compiled with Clang 3.9.1 or GCC 6.3.0 throwing movable but not copyable objects seems to work fine:
struct MovableNonCopyable {
MovableNonCopyable();
~MovableNonCopyable();
MovableNonCopyable(MovableNonCopyable &&);
MovableNonCopyable(MovableNonCopyable const &) = delete;
MovableNonCopyable & operator=(MovableNonCopyable &&);
MovableNonCopyable & operator=(MovableNonCopyable const &) = delete;
};
void f() { throw MovableNonCopyable(); }
But throwing copyable but not movable objects like this:
struct CopyableNonMovable {
CopyableNonMovable();
~CopyableNonMovable();
CopyableNonMovable(CopyableNonMovable &&) = delete;
CopyableNonMovable(CopyableNonMovable const &);
CopyableNonMovable & operator=(CopyableNonMovable &&) = delete;
CopyableNonMovable & operator=(CopyableNonMovable const &);
};
void g() { throw CopyableNonMovable(); }
instead causes compilation error like:
test.cpp: In function 'void g()':
test.cpp:21:41: error: use of deleted function 'CopyableNonMovable::CopyableNonMovable(CopyableNonMovable&&)'
void g() { throw CopyableNonMovable(); }
^
test.cpp:15:9: note: declared here
CopyableNonMovable(CopyableNonMovable &&) = delete;
^~~~~~~~~~~~~~~~~~
Why is this? According to [except.throw#5] this should be the other way around, i.e. the copy constructor should be accessible.
Here, you are explicitly asking the compiler to prevent construction from rvalue objects.
When you throw your temporary CopyableNonMovable()
object, the compiler looks for the appropriate constructor for its "copy" it has to throw. The declared constructor that fits best is the move constructor, since rvalues bind best to rvalue references. It looks at the declaration, sees it as deleted, and therefore has to refuse it.
The best solution is to simply not declare the move constructor, which will make it implicitly not generated, since a copy constructor was declared. In that case, the rvalues will bind best to the reference to const CopyableNonMoveable