I've being testing with GCC 5.2 and clang 3.6, both in C++14 mode, and they give the same output.
For the following code
#include <iostream>
#include <type_traits>
struct S {
// S& operator= (S&&) noexcept { return *this; }
};
int main() {
std::cout << std::is_nothrow_move_constructible<S>::value
<< std::is_nothrow_move_assignable<S>::value;
}
the result 11
is obtained. But if uncomment the move assignment operator, the output becomes 01
. How could an explicit noexcept
specification on the move assignment operator possibly affect that of the move constructor?
By defining the move assignment operator, you disabled the move constructor due to the rule of 5. The class isn't is_nothrow_move_constructible
because it isn't move constructable at all, that constructor is no longer available unless you define it.
§12.8 Copying and moving class objects
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.
In the case where you had no user-defined move constructor, both were implicitly defined and followed the below specification.
§15.4 Exception specifications
An implicitly declared special member function shall have an exception-specification. If
f
is 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
shall allow all exceptions if any function it directly invokes allows all exceptions, andf
shall allow no exceptions if every function it directly invokes allows no exceptions.
The move constructor is simply not generated in this case - it has nothing to do with noexcept
.
From cppreference:
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 (until C++14)
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 a non-explicit inline public member of its class with the signature T::T(T&&).
12.8/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, and
X
does not have a user-declared destructor.
By declaring a move assignment operator, you prevent the class from having any move constructor at all.
By defining the move operator, you've suppressed the implicit move constructor. That's why std::is_nothrow_move_constructible
fails. Provide it to get the desired output:
struct S {
S(S&&) noexcept {}
S& operator= (S&&) noexcept { return *this; }
};
来源:https://stackoverflow.com/questions/33057562/how-could-the-exception-specifier-on-move-assignment-operator-affect-that-of-mov