【问题标题】:How to use noexcept in assignment operator with copy-and-swap idiom?如何在具有复制和交换习语的赋值运算符中使用 noexcept?
【发布时间】:2013-09-21 19:05:04
【问题描述】:

移动赋值运算符通常应声明为 noexcept(即将类型存储在 STL 容器中)。但是复制和交换习惯用法允许在单个代码中定义复制和移动赋值运算符。在这种情况下如何处理 noexcept 说明符?复制构造可以抛出,但我怀疑它是否会违反 noexcept 说明符。

// Is it correct considering that T copy constructor can throw?
T& operator=(T other) noexcept;

【问题讨论】:

  • 这里的noexcept 很好。请注意,is_nothrow_*_constructible 特征仍将返回正确的值,因为它们的实现类似于noexcept(declval<T&>() = declval<T const&>()),它还考虑了复制或移动构造函数是否抛出(因为复制/移动需要碰巧按值传递)。

标签: c++ c++11 noexcept copy-and-swap


【解决方案1】:

由于副本是在调用的调用者的方面制作的,因此它不是您的函数所做的一部分。因此,它不能由您的函数控制,因此,您不能将此信息包含在 noexcept 规范中。

您唯一能做的就是稳妥行事,并将both 选项添加到您的noexcept 规范中。当然,这意味着你得到了一些假阴性。

【讨论】:

  • 你的意思是在将py值传递给函数时复制构造函数中抛出的异常不是该函数的一部分,因此可以安全地声明为noexcept?
  • @lizarisk “安全”是一个强词。基本上,我会同意。问题是 调用者 必须知道他自己的noexcept 规范需要什么。但是,从您的角度来看,这是一个 SEP(其他人的问题)。
  • 我会回答“是的,它是安全的”,就像“是正确的”IFF 身体确实没有抛出(这是交换的情况)。证明这一点的示例代码:godbolt.org/z/bzLOGA。请注意特征 is_nothrow_* 如何仍然返回正确的值。
【解决方案2】:

像往常一样,Daniel Freycorrect。我只想展示一段代码来说明这一点。

#include <iostream>

struct foo {

    foo() = default;

    foo(const foo&) {
        std::cout << "throw\n";
        throw 1;
    }

    foo& operator =(foo) noexcept {
        return *this;
    }
};

int main() {

    foo f, g;
    try {
        f = g; // throws
    }
    catch(int) {
        std::cout << "catch\n";
    }
}

当使用 gcc 4.8.1 (-std=c++11 -Wall -Wextra -pedantic) 编译时,它不会给出警告。运行代码会产生以下输出:

throw
catch

因此,复制构造函数在调用时确实会抛出,但在 operator =() 中没有考虑到这一点,因此,noexcept 承诺已实现。否则,将在打印出catch 之前调用终止。

【讨论】:

猜你喜欢
  • 2015-07-21
  • 1970-01-01
  • 1970-01-01
  • 2011-11-22
  • 1970-01-01
  • 2016-03-02
  • 2015-12-14
  • 2021-11-25
  • 2014-01-03
相关资源
最近更新 更多