对于具有抛出复制构造函数和noexcept-by-value复制赋值的类,is_nothrow_copy_assigna
What is the value of is_nothrow_copy_assignable for a class with a throwing copy constructor and a noexcept by-value copy assignment?
根据C++标准,以下程序的预期输出(如果有的话)是什么:
#include <iostream>
#include <iomanip>
#include <type_traits>
class A {
public:
A() = default;
~A() = default;
A(A const& other) {}
A(A&& other) noexcept {}
A& operator=(A other) noexcept { return *this; }
};
int main() {
std::cout << std::boolalpha
<< std::is_nothrow_copy_assignable<A>::value << "n"
<< std::is_nothrow_move_assignable<A>::value << "n";
}
换句话说,类型特征值的评估是否只考虑赋值运算符的声明,这是不例外的,因此是否会产生
true
true
还是考虑了调用上下文(a
、b
是A
的实例)
a = b; // may throw, implicitly calls copy c'tor
a = std::move(b); // noexcept, implicitly calls move c'tor
它能产生吗
false
true
实际尝试
使用Visual Studio 2015运行代码,更新3提供
true
true
而gcc 6.1给出了
false
true
谁是对的?
背景
当我们有一个带有抛出复制构造函数(因为资源分配可能会失败)、noexcept移动构造函数、抛出复制赋值和noexception移动赋值的资源管理类时,就会出现这样的情况。
假设复制和移动分配都可以根据交换idom:有效地实现
A& operator=(A const& other) {
A(other).swap(*this); // calls the copy c'tor, may throw
return *this;
}
A& operator=(A&& other) noexcept {
A(std::move(other)).swap(*this); // calls noexcept move c'tor
return *this;
}
然后我们可以考虑将两者压缩为按值拷贝分配
A& operator=(A other) noexcept {
other.swap(*this);
return *this;
}
然而,只有当std::is_nothrow_copy_assignable<A>
和std::is_nothrow_move_assignable<A>
提供正确的值(分别为false和true)时,我们才能安全地做到这一点。否则,依赖于这些类型特征的代码将表现不佳,并且我们的单值赋值将而不是是两个独立赋值运算符的正确替代。
is_nothrow_copy_assignable
的定义在[meta.unary.prop]中:
对于可引用类型
T
,结果与is_nothrow_assignable_v<T&, const T&>
相同,否则为false
。
好的,A
是可引用的(意味着A&
是有效的)。所以我们进入is_nothrow_assignable
:
is_assignable_v<T, U>
是true
,已知赋值不会引发任何异常(5.3.7)。
is_assignable_v<A, A const&>
肯定是true
,所以我们满足第一部分。众所周知不抛出任何异常意味着什么?根据[expr.unary.noexcept]:
noexcept
运算符确定其操作数的求值是否可以引发异常(15.1),该操作数是未求值的操作数(第5条)。[…]如果表达式(15.4)的潜在异常集为空,则noexcept
运算符的结果为true
,否则为false。
并且在〔except.spec〕中:
异常规范
noexcept
或noexcept(constant-expression)
,其中常量表达式产生true
,表示作为空集的异常规范。异常规范noexcept(constant-expression)
,其中常量表达式产生false
,或者在除析构函数(12.4)或释放函数(3.7.4.2)之外的函数声明符中不存在异常规范表示异常规范,它是所有类型的集合。
和:
如果e是核心常量表达式(5.20),则表达式e的潜在异常集为空。否则,它是e的直接子表达式的潜在异常集的并集,包括default函数调用中使用的参数表达式,与e形式定义的集合S组合,如下所示:[…]
--如果e隐式调用一个或多个函数(例如重载运算符、新表达式中的分配函数,或者如果e是完整表达式(1.9)则为析构函数),则S是以下函数的并集:
nbsp --所有此类函数的异常规范中的类型集,以及
nbsp nbsp;--如果e是一个新的表达式〔…〕
现在,从A const&
分配A
涉及两个步骤:
- 调用
A
的复制构造函数 - 调用
A
的复制分配运算符
异常规范是这两个函数的所有异常规范的并集,这是所有类型的集合,因为复制构造函数根本没有异常规范。
因此,is_nothrow_copy_assignable_v<A>
应该是false
。gcc是正确的。
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 在C++程序中输入的文本文件将不起作用,除非文本被复制和粘贴
- 使用strcpy将char数组的元素复制到另一个数组
- 是否可以初始化不可复制类型的成员变量(或基类)
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- C++ Windows 驱动程序MSB3030无法复制该文件,因为它找不到
- 复制列表初始化的隐式转换的等级是多少
- 当从函数参数中的临时值调用复制构造函数时
- 有可能在Armadillo中复制MATLAB circshift方法吗
- 复制几乎为空的数组的最快方法
- 以下示例中如何避免代码复制?C++/库达
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 不能将复制初始化与隐式转换的多个步骤一起使用
- Why is UINT32_MAX + 1 = 0?
- C++错误:"error: int aaa::bbb is protected within this context"
- 当有分配器意识的容器被复制/移动时,反弹分配器是否被复制/移走
- 为什么复制而不是移动数据元素?
- 文件系统:复制功能的速度秘诀是什么
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗