20

I have the following snippet:

#include <algorithm>
#include <iostream>

int main(int argc, char** argv) {
    int x[2][3];
    int y[2][3];

    using std::swap;
    std::cout << noexcept(swap(x, y)) << "\n";

    return 0;
}

Using GCC 4.9.0, this prints 0. I don't understand why.

According to the standard there's two overloads for std::swap:

namespace std {
    template<class T> void swap(T& a, T& b) noexcept(
        is_nothrow_move_constructible<T>::value &&
        is_nothrow_move_assignable<T>::value
    );

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

In my understanding the noexcept specifier for the array should work recursively for multidimensional arrays.

Why is swapping multidimensional arrays not noexcept?


While trying to find a minimal example that still behaves weirdly I came up with the following:

#include <iostream>

template<class T> struct Specialized      : std::false_type {};
template<>        struct Specialized<int> : std::true_type  {};

template<class T>                void f(T& a) noexcept(Specialized<T>::value);
template<class T, std::size_t N> void f(T (&a)[N]) noexcept(noexcept(f(*a)));

int main(int argc, char** argv) {
    int x, y[1], z[1][1];

    std::cout << noexcept(f(x)) << " "
              << noexcept(f(y)) << " "
              << noexcept(f(z)) << "\n";
}

Using GCC 4.9.0 this prints 1 1 0, but again I don't understand why.

orlp
  • 112,504
  • 36
  • 218
  • 315
  • 1
    [clang say 1](http://coliru.stacked-crooked.com/a/9d1c1e7307dda039) – Bryan Chen Nov 07 '14 at 04:31
  • Seems very strange to me. I found [DR 809](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#809) which [appears](https://gcc.gnu.org/onlinedocs/gcc-4.9.0/libstdc++/api/a01316_source.html#l00185) to be [implemented](https://gcc.gnu.org/onlinedocs/libstdc++/manual/bugs.html) in libstdc++ so maybe the bug lies elsewhere. Per your latest edit, [clang](http://coliru.stacked-crooked.com/a/79dd288d30d8f159) also prints `1 1 0`. clang with libc++ prints `0` for the original snippet. –  Nov 07 '14 at 05:20
  • [Coliru](http://coliru.stacked-crooked.com/a/7fa0b200e56d1bd4), [2](http://coliru.stacked-crooked.com/a/485e10b802dd3a96) –  Nov 07 '14 at 05:34
  • After running it through a debugger, for libstdc++, it goes through both overloads of `std::swap` and `std::move` which is marked noexcept. The fact that clang and g++ have different results for the exact same library code is boggling my mind. Note that with `noexcept` the code for `std::swap` isn't even generated. –  Nov 07 '14 at 05:49

1 Answers1

12

This overload:

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

is not in scope until ;, so swap(*a, *b) doesn't consider this overload. This is because of:

3.3.2/1 The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any) …

and the exception specification is part of the declarator.

  • 3
    This seems a pretty serious oversight in the standard. In its current form naive recursive `noexcept` declarations do not work. Worse even, the standard itself uses these naive recursive declarations. – orlp Nov 09 '14 at 15:24
  • 5
    Tell Santa; he'll put Bjarne on the naughty list. –  Nov 09 '14 at 15:24
  • 1
    @FrankHB This question predates the defect by a week. – orlp Sep 01 '16 at 13:22
  • For anyone who has stumbled upon this in 2021, it's fixed: https://cplusplus.github.io/LWG/issue2554 – thc Sep 11 '21 at 21:07