3

The standard library defines an swaps for arrays and std::pair like so:

template <class T, size_t N>
void swap(T (&a)[N],
          T (&b)[N]) noexcept(noexcept(swap(*a, *b)));

template <class T1, class T2>
struct pair {
    …
    void swap(pair& p) noexcept(noexcept(swap(first,  p.first)) &&
                                noexcept(swap(second, p.second)));
    …
};

Effective Modern C++ Item 14 says:

[…] whether they are noexcept depends on whether the expression inside the noexcept clauses are noexcept.
Given two arrays of Widget, for example, swapping them is noexcept only if swapping individual elements in the arrays is noexcept, i.e. if swap for Widget is noexcept.
That, in turn, determines whether other swaps, such as the one for arrays of arrays of Widget, are noexcept.
Similarly, whether swapping two std::pair objects containing Widgets is noexcept depends on whether swap for Widgets is noexcept.

But from this explanation, I cannot understand why it's necessary to nest the call as noexcept(noexcept(swap(*a, *b))).

If the goal is "swapping two arrays should be as noexcept as swapping elements", why doesn't noexcept(swap(*a, *b)) suffice?

Are these different overloads of noexcept() or something?

Birchlabs
  • 7,437
  • 5
  • 35
  • 54

1 Answers1

9

This is not "recursive" noexcept, there simply are two uses of the noexcept keyword:

  • noexcept specifier - which marks a function as noexcept and optionally takes a boolean constant expression as an argument

  • noexcept operator - which takes an expression as an argument and returns a boolean constant expression representing whether or not the expression is noexcept


In your case:

//          `noexcept` operator
//          v~~~~~~~~~~~~~~~~~~~~~
   noexcept(noexcept(swap(*a, *b)))
// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// `noexcept` specifier
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416