为什么交换多维数组不是noexcept?

20

我有以下代码片段:

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

使用GCC 4.9.0,这将打印0。我不明白为什么。
根据标准,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)));
}

在我看来,noexcept修饰符对于数组应该递归地适用于多维数组。

为什么交换多维数组不是noexcept


在尝试找到一个最小的示例来演示奇怪行为时,我想到了以下内容:

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

使用GCC 4.9.0编译后,这将打印出1 1 0,但我不太理解为什么会这样。

1
clang say 1 - Bryan Chen
对我来说似乎很奇怪。我找到了DR 809,它在libstdc++中被实现,所以也许这个错误存在于其他地方。根据您最新的编辑,clang也会打印出1 1 0。使用libc++的clang为原始片段打印出0 - user3920237
Coliru2 - user3920237
运行调试器后,对于libstdc++,它会通过std::swapstd::move的两个重载函数,并且标记为noexcept。clang和g++对完全相同的库代码有不同的结果,这让我感到困惑。请注意,使用noexcept std::swap 的代码甚至都不会生成。 - user3920237
1个回答

12

这个重载函数:

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

在分号;之前,变量名并没有被定义,因此swap(*a, *b)不会考虑这种重载情况。这是由于以下原因:

3.3.2/1 变量名的声明点紧接着它的完整声明符(第8条)之后,并在其初始化程序(如果有)之前...

而异常规范是声明符的一部分。


3
标准中存在一个相当严重的疏忽。目前,天真递归的noexcept声明无法正常工作。更糟糕的是,标准本身使用了这些天真递归的声明。 - orlp
5
告诉圣诞老人,他会把Bjarne列入调皮名单。 - user1804599
1
@FrankHB 这个问题比缺陷早一周。 - orlp
对于在2021年偶然发现这个问题的任何人,它已经被修复了:https://cplusplus.github.io/LWG/issue2554 - thc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接