M.M*_*M.M 2 c++ exception language-lawyer
这是否作为异常处理程序有效,哪些T是具有非const成员函数的类func?
换句话说:是否catch保证直接绑定到(可修改的)异常对象,或者当你通过const引用捕获时,编译器是否有自由做一些技巧?
catch(const T &t)
{
const_cast<T &>(t).func();
}
Run Code Online (Sandbox Code Playgroud)
(我正在使用C++ 11;我需要更新;))
15.1/3(强调我的):
甲界外球表达初始化临时对象,称为异常对象,其中所述类型的通过去除任何顶层CV-合格音响ERS确定从静态式抛的操作数的
这意味着异常对象永远不会"生成const",因此不应触发未定义的行为进行修改.
来自[except.throw]:
使用操作数计算throw-expression会抛出异常(15.1); 异常对象的类型是通过从操作数的静态类型中删除任何顶级cv限定符并将类型从"T的数组"或"返回T的函数"调整为"指向T的指针"或"指针"来确定的.函数返回T",分别.
并强调我的:
抛出异常copy-initializes(8.5,12.8)一个临时对象,称为异常对象.临时是一个左值,用于初始化匹配处理程序中声明的变量(15.3).
因此,如果我们抛出类型为cv T 的操作数,我们将复制初始化类型为T的临时对象.
然后根据[except.handle],const T&(对于非指针类型T)的处理程序匹配E类型的异常对象,如果:
- [...] E和T是相同的类型(忽略顶级cv限定符),
- [...] T是E的明确公共基类
此处理程序初始化为:
异常声明声明的类型为cv T或cv T&的变量是从类型E的异常对象初始化的,如下所示:
- 如果T是E的基类,则变量是复制初始化的(8.5)来自异常对象的相应基类子对象;
- 否则,变量从异常对象复制初始化(8.5).
因此,如果我们通过const T&捕获,我们将从异常对象复制初始化引用 - 我们从上一节中知道它将是T类型或从T公开派生.来自[dcl.init.ref]:
对类型" cv1 T1"的引用由类型" cv2 T2" 的表达式初始化,如下所示:
- 如果引用是左值引用,则初始化表达式
- 是左值(但不是位字段),并且" cv1 T1"与" cv2 T2" 参考兼容,或[...]然后在第一种情况下将引用绑定到初始化表达式lvalue
关键是临时异常对象仍然是左值.因此,如果我们的处理程序与const T&匹配,我们知道引用直接绑定到类型为T或D的对象(其中D派生自T) - 无论哪种方式,它都是与引用兼容的类型const T.因此,没有未定义的行为.如果临时对象是rvalue或者处理程序可以匹配更广泛的类型,那么将创建一个临时类型const T- 并且您const_cast肯定会是未定义的行为.
虽然您的代码在符合标准的编译器上没有显示未定义的行为,但实际上没有理由不这样做:
catch(T &t)
{
t.func();
}
Run Code Online (Sandbox Code Playgroud)