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.

4
  • 1
    clang say 1 Commented Nov 7, 2014 at 4:31
  • Seems very strange to me. I found DR 809 which appears to be implemented in libstdc++ so maybe the bug lies elsewhere. Per your latest edit, clang also prints 1 1 0. clang with libc++ prints 0 for the original snippet. Commented Nov 7, 2014 at 5:20
  • Coliru, 2 Commented Nov 7, 2014 at 5: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. Commented Nov 7, 2014 at 5:49

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

4 Comments

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.
Tell Santa; he'll put Bjarne on the naughty list.
@FrankHB This question predates the defect by a week.
For anyone who has stumbled upon this in 2021, it's fixed: cplusplus.github.io/LWG/issue2554

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.