Is the default Move constructor defined as noexcept?
I think the answer is 15.4/14 (Exception specifications):
An inheriting constructor (12.9) and an implicitly declared special member function (Clause 12) have an exception-specification. If
f
is an inheriting constructor or an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-idT
if and only ifT
is allowed by the exception-specification of a function directly invoked byf
’s implicit definition;f
allows all exceptions if any function it directly invokes allows all exceptions, andf
has the exception-specificationnoexcept(true)
if every function it directly invokes allows no exceptions.
Basically, it Does What You Think, and the implicitly-declared move constructor is noexcept
whenever it can be.
Related videos on Youtube
Comments
-
bjackfly over 4 years
It seems that a vector will check if the move constructor is labeled as noexcept before deciding on whether to move or copy elements when reallocating. Is the default move constructor defined as noexcept? I saw the following documentation but it didn't specify this.http://en.cppreference.com/w/cpp/language/move_constructor
Implicitly-declared move constructor
If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true: there are no user-declared copy constructors there are no user-declared copy assignment operators there are no user-declared move assignment operators there are no user-declared destructors the implicitly-declared move constructor is not defined as deleted due to conditions detailed in the next section then the compiler will declare a move constructor as an inline public member of its class with the signature T::T(T&&) A class can have multiple move constructors, e.g. both T::T(const T&&) and T::T(T&&). If some user-defined move constructors are present, the user may still force the generation of the implicitly declared move constructor with the keyword default.
-
Howard Hinnant almost 11 yearsAdditional information: And you can test whether or not your expectations have been met:
static_assert(std::is_nothrow_move_constructible<MyType>::value, "MyType should be noexcept MoveConstructible");
-
mucaho over 8 yearsSo all functions invoked by the implicit special member functions must be declared
noexcept
for the implicit special member functions to benoexcept
. Means you have to be diligent enough to mark all relevant functionsnoexcept
, means there is a lot room for human error, right? -
Kerrek SB over 8 years@mucaho: Well, if all your members themselves only use implicitly defined special members, then this isn't all that complex. The simple rule is the rule of single responsibility, and by default the only human error you should watch out for is defining special member functions explicitly. That only leaves special-purpose classes (such as
unique_ptr
) that you need to vet. -
Yan Zhou over 7 yearsWhat about explicitly declared special member function with
default
? Such asvoid T(T &&) = default
. Am I correct to assume that it behaves exactly the same if this move constructor is implicitly declared if not prevented by other conditions, such as a user-defined copy constructor? -
Deduplicator almost 6 years@YanZhou Yes, explicitly defaulted Special member functions also follow those rules unless explicitly overridden for
constexpr
andnoexcept
. Your example has a spuriousvoid
though, so it is a compile-error. -
Louis Semprini almost 4 years@HowardHinnant Your helpful comment to
static_assert(std::is_nothrow_move_constructible<MyType>::value)
is a little misleading because that check will pass even ifMyType
has anothrow/noexcept
copy constructor (in which case the compiler makes no default move constructor, thus probably not being what the programmer expects since thenstd::vector
copies). This includes both the cases of explicitly written AND compiler-generated implicit copy constructor if they happen to benoexcept
....(continued) -
Louis Semprini almost 4 years@HowardHinnant (continued)...so given that rub, I'm not sure if the check is useful in the cases where the programmer hasn't explicitly also written a copy constructor that they know is not
noexcept
(because it's too much brainwork to figure out if the compiler-generated move-constructor would exist (despite your incredibly useful video youtube.com/watch?v=vLinb2fgkHk) and also whether the compiler-generated copy constructor would benoexcept
). It's too bad there is no direct check that ONLY looks at the move constructor. -
Howard Hinnant almost 4 years
vector
doesn't literally "look at the move constructor". In all likelihood it callsmove_if_noexcept
, or some equivalent: github.com/llvm/llvm-project/blob/master/libcxx/include/…move_if_noexcept
branches onis_nothrow_move_constructible
: eel.is/c++draft/forward#7 -
Louis Semprini almost 4 years@HowardHinnant Right, it doesn't (confirmed by vimeo.com/97337253 at 38:05), but I'm saying neither does
std::is_nothrow_move_constructible*
, so that's why the test might be misleading to the OP and others...even if that test is true, meaning thatvector
will "do the right thing" by callingMyType(std::move(bar))
rather thanMyType(bar)
, it turns out the call still ends up getting routed to the contained element's copy constructor, not its move constructor (continued)... -
Louis Semprini almost 4 years@HowardHinnant I think most readers of this question will assume the test checks whether the move constructor is called, but that's not the case. It would be nice if there could be such a test in C++, but it seems like the ideal test is not possible. So it would be good to warn people of the limitations of the (still useful)
std::is_nothrow_move_constructible*
test. -
Howard Hinnant almost 4 yearsThe test answers whether or not the expression
MyType(std::move(bar))
is a valid,noexcept
expression. This is the expression thatvector
will use. Asking if a move member exists or not is a different issue, and not nearly as helpful in generic programming. -
Louis Semprini almost 4 years@HowardHinnant Right, but the test people are hoping for isn't really "which path does
std::vector
take?" but rather "when all is said and done, will my MyType objects get copied or moved?" It's very easy for us to be fooled into a false confidence that our data is being moved especially in two cases: case #1: we wrote a copy constructor and didn't look at your cool video and understand that the compiler would not provide an implicit move constructor for us, and (continued) -
Louis Semprini almost 4 years@HowardHinnant case #2 we provided a destructor, causing the compiler to provide an implicit copy constructor (that could be noexcept) and omit the move constructor. In both cases we could easily assume that the compiler is providing a move constructor, and the
std::is_nothrow_move_constructible*==true
result could reinforce our false idea. I'll agree that this is a separate issue of "if you write one special function you should write all," however that second issue is likely to trip us up that I think it's worth mentioning whenever mentioning thestd::is_nothrow_move_constructible*
test. -
Howard Hinnant almost 4 yearsType authors need to give loving care to all 6 special members, whether or not they let the compiler provide them, and whether or not that type is to be used as an element in vector: howardhinnant.github.io/classdecl.html
-
Louis Semprini almost 4 years@HowardHinnant Haha yes agreed! Glad that we now have a link to that guide on this question.