这个问题是"Constructor with by-value parameter & noexcept" 的对偶。该问题表明,按值函数参数的生命周期管理由调用函数处理;因此调用者处理发生的任何异常,并且被调用函数可以标记自己noexcept
。我想知道如何处理输出端noexcept
。
MyType MyFunction( SomeType const &x ) noexcept;
//...
void MyCaller()
{
MyType test1 = MyFunction( RandomSomeType() );
MyType test2{ MyFunction( RandomSomeType() ) };
//...
test1 = MyFunction( RandomSomeType() );
test2 = std::move( MyFunction(RandomSomeType()) );
MyFunction( RandomSomeType() ); // return value goes to oblivion
}
假设返回值在MyFunction
. 假设适当的特殊成员函数(复制/移动分配/构造)MyType
可能不是noexcept
.
- 关于将返回值从被调用函数传输到调用者的 RVO/NRVO/Whatever-from-C++11 规则是否意味着无论
noexcept
相应的特殊成员函数的状态如何,传输总是成功不抛出? - 如果上一个问题的答案是“否”,那么如果返回值传输抛出异常,该异常是否计入被调用函数或调用者?
- 如果上一个问题的答案是“被调用函数”,那么简单的
noexcept
标记 onMyFunction
将导致调用std::terminate
.MyFunction
的noexcept
个人资料应该改成什么?当我在 Usenet 上询问这个问题时,一位受访者认为应该是std::is_nothrow_move_assignable<MyType>::value
. (请注意,MyCaller
使用了几种使用返回值的方法,但MyFunction
不知道正在使用哪一种!答案必须涵盖所有情况。)如果MyType
更改为可复制但不可移动,是否会有所不同?
noexcept
因此,如果第二个和第三个问题的最坏情况是准确的,那么如果返回类型具有可抛出的移动,则任何按值返回的函数都不能有一个平原!现在具有可抛出动作的类型应该很少见,但模板代码仍然必须在is_nothrow_move_assignable
每次使用按值返回时“弄脏”自己。
我认为让被调用函数负责是坏的:
MyType MyFunction( SomeType const &x ) noexcept( ??? )
{
//...
try {
return SOME_EXPRESSION;
// What happens if the creation of SOME_EXPRESSION succeeds, but the
// move-assignment (or whatever) transferring the result fails? Is
// this try/catch triggered? Or is there no place lexically this
// function can block a throwing move!?
} catch (...) {
return MyType();
// Note that even if default-construction doesn't throw, the
// move-assignment may throw (again)! Now what?
}
}
至少对我来说,这个问题在调用者端似乎是可以解决的(只需用try
/包装移动分配catch
),但从被调用函数端无法解决。我认为调用者必须处理这个问题,即使我们需要更改 C++ 的规则来这样做。或者至少需要某种缺陷报告。