标记一个可能导致构造返回对象异常的函数“noexcept”
Marking a function `noexcept` that could cause an exception constructing the returned object
考虑这个函数:
std::vector<unsigned> copy(std::vector<unsigned> const& v) noexcept
{ return v; }
int main()
{
try {
(void)copy({1, 2, 3});
} catch(...) {}
}
通过返回对象的副本构造可能会抛出异常。在这种情况下,异常是否会传播给调用者(即它被认为发生在 main
中),因此会在 catch(...)
处理程序中处理?或者异常 运行 进入 noexcept
并导致调用 std::terminate()
?
C++17/C++20 中有关生命周期规则的更改(标准化 RVO、临时物化、隐式对象创建等)是否相对于标准的先前版本更改了这方面的一些规则?
C++17 有一个措辞更改,在 return 语句周围添加了顺序。添加了以下段落。
[stmt.return]
3 The copy-initialization of the result of the call is sequenced
before the destruction of temporaries at the end of the
full-expression established by the operand of the return statement,
which, in turn, is sequenced before the destruction of local variables
([stmt.jump]) of the block enclosing the return statement.
结果对象在范围内的局部变量被销毁之前被初始化。这意味着抛出在函数的范围内。因此,此时抛出的任何异常都不在调用方。
因此,将函数标记为 noexcept
将使程序终止。
RVO 不会改变这一点。它只影响结果对象在什么存储中被初始化,但初始化本身仍然是函数执行的一部分。
In this case, would the exception be propagated to the caller (i.e. it's considered to happen in main) and thus would be handled in the catch(...) handler?
我不同意。复制必须作为 return 语句表达式的一部分在函数范围内完成。因为局部析构函数仅在 return 之后被调用,并且它们明确地在函数范围内。
是的,C++17 对 RVO 做了一些保证,特别是这个例子现在保证省略:
struct Foo{};
Foo bar(){
Foo local;
return Foo{};
// Foo:~Foo(local);
}
Foo var = bar();
不过,如果 Foo:Foo()
抛出异常,则函数不是 noexcept。所有 RVO 都表示没有移动或复制到 var
变量和 Foo{}
表达式在位置 var
构造对象。然而,当构造对象时它不会改变 - 在函数范围内,甚至在调用析构函数之前。
此外,强制性 RVO 在这里不适用,因为 v
不是纯右值而是 l-value。
考虑这个函数:
std::vector<unsigned> copy(std::vector<unsigned> const& v) noexcept
{ return v; }
int main()
{
try {
(void)copy({1, 2, 3});
} catch(...) {}
}
通过返回对象的副本构造可能会抛出异常。在这种情况下,异常是否会传播给调用者(即它被认为发生在 main
中),因此会在 catch(...)
处理程序中处理?或者异常 运行 进入 noexcept
并导致调用 std::terminate()
?
C++17/C++20 中有关生命周期规则的更改(标准化 RVO、临时物化、隐式对象创建等)是否相对于标准的先前版本更改了这方面的一些规则?
C++17 有一个措辞更改,在 return 语句周围添加了顺序。添加了以下段落。
[stmt.return]
3 The copy-initialization of the result of the call is sequenced before the destruction of temporaries at the end of the full-expression established by the operand of the return statement, which, in turn, is sequenced before the destruction of local variables ([stmt.jump]) of the block enclosing the return statement.
结果对象在范围内的局部变量被销毁之前被初始化。这意味着抛出在函数的范围内。因此,此时抛出的任何异常都不在调用方。
因此,将函数标记为 noexcept
将使程序终止。
RVO 不会改变这一点。它只影响结果对象在什么存储中被初始化,但初始化本身仍然是函数执行的一部分。
In this case, would the exception be propagated to the caller (i.e. it's considered to happen in main) and thus would be handled in the catch(...) handler?
我不同意。复制必须作为 return 语句表达式的一部分在函数范围内完成。因为局部析构函数仅在 return 之后被调用,并且它们明确地在函数范围内。
是的,C++17 对 RVO 做了一些保证,特别是这个例子现在保证省略:
struct Foo{};
Foo bar(){
Foo local;
return Foo{};
// Foo:~Foo(local);
}
Foo var = bar();
不过,如果 Foo:Foo()
抛出异常,则函数不是 noexcept。所有 RVO 都表示没有移动或复制到 var
变量和 Foo{}
表达式在位置 var
构造对象。然而,当构造对象时它不会改变 - 在函数范围内,甚至在调用析构函数之前。
此外,强制性 RVO 在这里不适用,因为 v
不是纯右值而是 l-value。