Code:
#include <iostream>
#include <ios>
#include <string>
#include <type_traits>
#include <memory>
struct value
{
~value() = default;
std::unique_ptr<std::string> s;
};
int main()
{
std::cout << std::boolalpha;
std::cout << std::is_move_constructible<value>::value << '\n';
std::cout << std::is_move_assignable<value>::value << '\n';
using str_ptr = std::unique_ptr<std::string>;
std::cout << std::is_move_constructible<str_ptr>::value << '\n';
std::cout << std::is_move_assignable<str_ptr>::value << '\n';
return 0;
}
Output (compiled with g++ v4.7.2, http://ideone.com/CkW1tG):
false false true true
As I expect, value
is not move constructible and is not move assignable because:
~value() = default;
is a user-declared destructor, which prevents the implicit generation of move members according to section 12.8 (see below).
If the destructor is removed then value
is move constructible and move assignable, as I expect (http://ideone.com/VcR2eq).
However, when the definition of value
is changed to (http://ideone.com/M8LHEA):
struct value
{
~value() = default;
std::string s; // std::unique_ptr<> removed
};
the output is:
true true true true
value
is unexpectedly move constructible and move assignable.
Am I misunderstanding or is this a compiler bug?
Background: I provided an answer to this question and was informed that Tree<>
was moveable, but I am unsure and am attempting to determine for certain if it is or not.
Section 8.4.2 Explicitly-defaulted functions of the c++11 standard (draft n3337):
Explicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions, and the implementation shall provide implicit definitions for them (12.1 12.4, 12.8), which might mean defining them as deleted. A special member function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed. [ Note: Declaring a function as defaulted after its first declaration can provide efficient execution and concise definition while enabling a stable binary interface to an evolving code base.—end note ]
Section 12.8 Copying and moving class objects (point 9):
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if - X does not have a user-declared copy constructor, - X does not have a user-declared copy assignment operator, - X does not have a user-declared move assignment operator, - X does not have a user-declared destructor, and - the move constructor would not be implicitly defined as deleted.
If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then: No move constructor is automatically generated. No move-assignment operator is automatically generated.
Move assignment operators typically "steal" the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc.), rather than make copies of them, and leave the argument in some valid but otherwise indeterminate state.
The special member functions are class (or struct) member functions that, in certain cases, the compiler automatically generates for you. These functions are the default constructor, the destructor, the copy constructor and copy assignment operator, and the move constructor and move assignment operator.
Explanation: There are basically 5 types of member functions possible. The types include simple, static, const, inline, and friend member functions.
std::is_move_constructible<T>
is true iff std::is_constructible<T, T&&>
is true, but that doesn't imply that such a construction will call a move constructor, only that it is possible to construct the type from an rvalue of the same type. Such a construction might use a copy constructor.
When value::s
is a unique_ptr
the type's copy constructor and copy assignment operator are defined as deleted, because the s
member is not copyable. It does not have a move constructor and move assignment operator because, as you pointed out, it has a user-declared destructor. That means it has no copy constructor and no move constructor (and no other user-defined constructors that could accept an argument of type value&&
) so std::is_constructible<value, value&&>
is false.
When value::s
is a string
the type's copy constructor and copy assignment operator are not defined as deleted, because the s
member is copyable, and so value
is also copyable, and a CopyConstructible type is also MoveConstructible, because it's valid in this context:
value v1;
value v2 = std::move(v1); // calls copy constructor
That means std::is_constructible<value, value&&>
is true, even though it invokes the copy constructor not a move constructor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With