3

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?

Lingxi
  • 14,579
  • 2
  • 37
  • 93

5 Answers5

7

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-id T if and only if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition; f shall allow all exceptions if any function it directly invokes allows all exceptions, and f shall allow no exceptions if every function it directly invokes allows no exceptions.

Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
5

By declaring a move assignment, you've lost your implicit move constructor.
See the full chart below.

enter image description here

Trevor Hickey
  • 36,288
  • 32
  • 162
  • 271
  • Nice chart! BTW, why are those defaulted fields dark red? – LogicStuff Oct 10 '15 at 18:55
  • Because it's extremely dangerous. – Puppy Oct 10 '15 at 19:06
  • Can you provide a link? – LogicStuff Oct 10 '15 at 19:11
  • 1
    @LogicStuff The red squares indicate deprecated behavior starting after C++98/03. In newer standards, I believe they become "not declared", but I'm not too sure there is a guarantee on that. They appear not declared, in my version of clang. – Trevor Hickey Oct 10 '15 at 19:12
  • I guess I will just remember the first three rows. – Lingxi Oct 10 '15 at 19:12
  • 1
    Here is a thread involving Howard Hinnant, than man who made this table: http://stackoverflow.com/questions/24342941/what-are-the-rules-for-automatic-generation-of-move-operations Howard's presentation: http://accu.org/content/conf2014/Howard_Hinnant_Accu_2014.pdf – Trevor Hickey Oct 10 '15 at 19:12
  • 2
    Nice chart! Thanks for the link. Red means deprecated in C++11 and forward. They have not been removed, but they might be in a future standard. Guideline: Don't depend on deprecated behavior. If you explicitly declare any one of the destructor, copy constructor, or copy assignment, then explicitly declare both the copy constructor and copy assignment. – Howard Hinnant Oct 10 '15 at 19:21
2

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&&).

Rostislav
  • 3,857
  • 18
  • 30
2

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.

Community
  • 1
  • 1
aschepler
  • 70,891
  • 9
  • 107
  • 161
1

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; }
};