jba*_*bab 5 c++ language-lawyer c++11 c++14
就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";
}
Run Code Online (Sandbox Code Playgroud)
换句话说,类型特征值的评估是仅仅查看赋值运算符的声明,这是noexcept,并且它因此产生
true
true
Run Code Online (Sandbox Code Playgroud)
或者它是否考虑调用上下文(a,b是实例A)
a = b; // may throw, implicitly calls copy c'tor
a = std::move(b); // noexcept, implicitly calls move c'tor
Run Code Online (Sandbox Code Playgroud)
它会屈服吗?
false
true
Run Code Online (Sandbox Code Playgroud)
实际尝试
使用Visual Studio 2015运行代码,Update 3给出
true
true
Run Code Online (Sandbox Code Playgroud)
而gcc 6.1给出了
false
true
Run Code Online (Sandbox Code Playgroud)
谁是对的?
背景
当我们有一个带有抛出复制构造函数的资源管理类(因为资源分配可能失败),noexcept移动构造函数,抛出副本赋值和noexcept移动赋值时,就会出现这种情况.
假设可以根据交换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;
}
Run Code Online (Sandbox Code Playgroud)
然后我们可以考虑将两者压缩成单个按值复制赋值
A& operator=(A other) noexcept {
other.swap(*this);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
但是,我们只能安全地执行此操作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操作员确定其操作数的评价,这是一个未计算的操作数(第5章)是否,可以抛出异常(15.1).[...]noexcept运算符的结果是true表达式(15.4)的潜在异常集合为空,否则为false.
在[except.spec]中:
异常规范
noexcept或noexcept(constant-expression)常量表达式产生true的异常规范表示空集的异常规范.除了析构函数(12.4)或释放函数(3.7.4.2)之外,函数声明符noexcept(constant-expression)中的常量表达式产生false或缺少异常规范的异常规范表示异常规范.各种类型.
和:
如果e是核心常数表达式,则表达式e的潜在异常集合为空(5.20).否则,它是e的直接子表达式的潜在异常集合的并集,包括函数调用中使用的默认参数表达式,以及由e形式定义的集合S,如下所示:[... ]
- 如果e隐式调用一个或多个函数(例如重载运算符,new表达式中的分配函数,或者如果e是完整表达式(1.9)则为析构函数),S是以下的并集:
- 集合所有这些函数的异常规范中的类型,以及
- 如果e是一个新表达式[...]
现在的分配A从A const&包括两个步骤:
AA异常规范是这两个函数的所有异常规范的并集,它是所有类型的集合 - 因为拷贝构造函数根本没有异常规范.
因此,is_nothrow_copy_assignable_v<A>应该是false.gcc是对的.
| 归档时间: |
|
| 查看次数: |
160 次 |
| 最近记录: |