Defined and not defined constructor in C++
C++ is a pretty interesting language because of different quirks that it has. One of such things is the fact that there is a difference between defined constructor, not defined constructor and deleted constructor, especially move constructor.
Consider following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | #include <iostream> struct ExplicitMoveConstructor { ExplicitMoveConstructor() { std::cout << "Default constructor" << std::endl; } ExplicitMoveConstructor(ExplicitMoveConstructor const& src) { std::cout << "Copy constructor" << std::endl; } ExplicitMoveConstructor(ExplicitMoveConstructor && src) { std::cout << "Move constructor" << std::endl; } }; struct DeletedMoveConstructor { DeletedMoveConstructor() { std::cout << "Default constructor" << std::endl; } DeletedMoveConstructor(DeletedMoveConstructor const& src) { std::cout << "Copy constructor" << std::endl; } DeletedMoveConstructor(DeletedMoveConstructor && src) = delete; }; struct NotDefinedMoveConstructor { NotDefinedMoveConstructor() { std::cout << "Default constructor" << std::endl; } NotDefinedMoveConstructor(NotDefinedMoveConstructor const& src) { std::cout << "Copy constructor" << std::endl; } }; auto returnExplicitMoveConstructorClass() { ExplicitMoveConstructor cm; return cm; } auto returnDeletedMoveConstructorClass() { DeletedMoveConstructor cm; return cm; } auto returnNotDefinedMoveConstructorClass() { NotDefinedMoveConstructor cm; return cm; } int main() { auto first = returnExplicitMoveConstructorClass(); auto second = returnDeletedMoveConstructorClass(); auto third = returnNotDefinedMoveConstructorClass(); return 0; } |
The presented code does not compile when trying to do so with following command:
clang++ constructor.cpp --std=c++14 |
The error code is as follows:
constructor.cpp:70:7: error: call to deleted constructor of 'DeletedMoveConstructor' auto second = returnDeletedMoveConstructorClass(); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ constructor.cpp:33:2: note: 'DeletedMoveConstructor' has been explicitly marked deleted here DeletedMoveConstructor(DeletedMoveConstructor && src) = delete; ^ 1 error generated. |
The error from the compiler says that the move constructor was called but it is marked as deleted. If compiled with g++ it’s pretty much the same.
Conclusions from this are two. The first one is that the deleted constructor is also a declared constructor so the compiler will use it as if it was defined by the user. The second one is that the value returned from function is returned by utilizing move semantic when move constructor is declared, even if it is deleted.
The explaination for this turns out to be quite simple. As written in this post on Stack Overflow, this is the intended behaviour of overload resolution. Since declared, the move constructor is the best match for value returned from function which is an r-value.
Leave a comment