Move Constructors without noexcept

#include <string>
#include <iostream>

class Person {
  private:
    std::string name;
  public:
    Person(const char* n)
     : name{n} {
    }

    std::string getName() const {
      return name;
    }

    // print out when we copy or move:
    Person(const Person& p)
     : name{p.name} {
        std::cout << "COPY " << name << '\n';
    }
    Person(Person&& p)
     : name{std::move(p.name)} {
        std::cout << "MOVE " << name << '\n';
    }
    //...
};

int main()
{
   std::vector<Person> coll{ "Wolfgang Amadeus Mozart",
                             "Johann Sebastian Bach",
                             "Ludwig van Beethoven" };
                             
   std::cout << "capacity: " << coll.capacity() << '\n';
   
   coll.push_back("Pjotr Iljitsch Tschaikowski");
}

The output of the program is as follows:

COPY Wolfgang Amadeus Mozart
COPY Johann Sebastian Bach
COPY Ludwig van Beethoven
capacity: 3
MOVE Pjotr Iljitsch Tschaikowski
COPY Wolfgang Amadeus Mozart
COPY Johann Sebastian Bach
COPY Ludwig van Beethoven

The following diagrams demonstrate how does push_back() work internally:

The reason that vector reallocation does not use move semantics is the strong exception handling guarantee we give for push_back(): When an exception is thrown in the middle of the reallocation of the vector the C++ standard library guarantees to roll back the vector to its previous state. Please see STL container with noexcept for more details.

Move Constructors with noexcept

When we guarantee that the move constructor never throw:

Person(Person&& p) noexcept
     : name{std::move(p.name)} {
        std::cout << "MOVE " << name << '\n';
    }

The output will be:

COPY Wolfgang Amadeus Mozart
COPY Johann Sebastian Bach
COPY Ludwig van Beethoven
capacity: 3
MOVE Pjotr Iljitsch Tschaikowski
MOVE Wolfgang Amadeus Mozart
MOVE Johann Sebastian Bach
MOVE Ludwig van Beethoven

Please see emplace_back vs. push_back for more details.

Details of noexcept Declarations

noexcept for Copying and Moving Special Member Functions

noexcept for Destructors

By rule, destructors always guarantee not to throw by default. This applies to both generated and implemented destructors. For example:

class B
{
private:
   std::string s;
public:
   ~B() {
       // automatically always declared as ~B() noexcept
   }
};

With noexcept(false), you can declare them without this guarantee, but that usually never makes any sense because several guarantees of the C++ standard library are based on the fact that destructors never throw.

noexcept Declarations in Class Hierarchies