我对C++0x FCD中的noexcept
背后的原理很好奇。虽然throw(X)
已经被废弃,但noexcept
似乎可以实现相同的功能。为什么noexcept
没有在编译时进行检查呢?如果这些函数能够在try
块内只调用可抛出异常的函数,那么静态检查它们似乎会更好。
我对C++0x FCD中的noexcept
背后的原理很好奇。虽然throw(X)
已经被废弃,但noexcept
似乎可以实现相同的功能。为什么noexcept
没有在编译时进行检查呢?如果这些函数能够在try
块内只调用可抛出异常的函数,那么静态检查它们似乎会更好。
noexcept(true)
- 可以说,这应该是一个严格的要求。在C ++中,抛出异常的析构函数始终是错误的。noexcept(true)
- 再次相同的论点:C语言异常总是错误的。noexcept(false)
4.noexcept(true)
函数必须将其所有noexcept(false)
调用包装在try {} catch(...){}
中
- 类比于const方法不能调用non-const方法。noexcept(true)
和noexcept(false)
版本,就像您可以重载const和非const版本的成员函数一样。noexcept(false)
的额外重整的noexcept(true)
版本。noexcept(false)
这将打破向后兼容性,
这可能是不可能的,参见第1点。我亲自与一个标准委员会成员谈过这件事,他说这是一个仓促的决定,主要是由于容器中移动操作的限制(否则您可能在抛出异常后丢失项目,从而违反基本保证)。请注意,这是一个声称不容忍故障代码是好的的人的设计哲学。得出你自己的结论。
就像我说的,我宁愿破坏ABI而不是破坏语言。 noexcept
只是对旧方式的微小改进。静态检查总是更好的。
The vast experience of the last two decades shows that in practice, only two forms of exceptions specifications are useful:
The lack of an overt exception specification, which designates a function that can throw any type of exception:
int func(); //might throw any exception
A function that never throws. Such a function can be indicated by a throw() specification:
int add(int, int) throw(); //should never throw any exception
noexcept
检查由失败的 dynamic_cast
和应用于 null
指针的 typeid
抛出的异常,这只能在运行时完成。其他测试确实可以在编译时完成。try
块之外禁止使用dynamic_cast
或typeid
是可行的... - rlbonddynamic_cast
这样的语句可能会抛出异常,但只能在运行时进行检查,因此编译器无法在编译时确定。dynamic_cast
,由于某种原因,您知道它永远不会失败并抛出异常,因为您的程序是这样编写的。编译器可能不知道这一点并发出警告,这变成了噪音,程序员可能只是将其禁用,因为它没有用,从而否定了警告的作用。noexcept
(即可能抛出异常)的函数,并且您想从许多函数中调用该函数,其中一些是 noexcept
,一些不是。您知道在 noexcept
函数调用的情况下该函数永远不会抛出异常,但编译器不知道:更多无用的警告。void fn() noexcept
{
foo();
bar();
}
你能进行静态检查,确定它是正确的吗?你需要知道foo或者bar是否会抛出异常。你可以强制所有的函数调用在try{}块中执行,例如:
void fun() noexcept
{
try
{
foo();
bar();
}
catch(ExceptionType &)
{
}
}
foo
和bar
具有“可能抛出”的规范时,fun
是否抛出取决于它们可能抛出的内容。如果它们抛出ExceptionType
对象,则fun
是可以的。如果它们抛出OtherExceptionType
对象,则fun
就不行了。这些原型不足以确定这一点。 - Dennis Zickefoosenoexcept
和throw()
的功能上有一些重叠,但它们实际上来自于相反的方向。throw()
关注的是正确性,而且仅仅是关注正确性:它是指定如果出现意外异常时的行为方式。 noexcept
的主要目的是优化:它允许/鼓励编译器在假定不会抛出异常的情况下进行优化。但是,实际上,它们并没有差别太大,可以视作同一种东西的两个不同名称。虽然背后的动机是不同的。noexcept
的主要目的是确保正确性。抛出异常会破坏之前正确代码(如排序算法和容器调整大小)中的强保证,因此我们需要 std::move_if_noexcept
来以安全的方式重写旧的通用代码。当然,缺乏静态检查使其表现得像 assert
而不是 const
,但这是向后兼容性的代价 :-( - spraffnoexcept
(以及等效的 throw()
)的存在或不存在而表现出不同的行为,因此它不仅关乎编译器优化,而且还影响库设计和算法选择。做到这一点的关键是使用 noexcept
运算符,它允许代码查询表达式的“抛出性”,_这就是_新东西,它只关心一个是/否的答案,不关心可能抛出的异常类型,只关心是否会抛出异常。 - Jonathan Wakely