通过const引用捕获异常并抛弃const

时间:2015-07-20 21:42:35

标签: c++ exception language-lawyer

这是否有效作为异常处理程序,其中T是某个具有非const成员函数func的类?

换句话说:catch是否保证直接绑定到(可修改的)异常对象,或者当你通过const引用捕获时,编译器是否有自由做一些技巧?

catch(const T &t)
{
    const_cast<T &>(t).func();
}

4 个答案:

答案 0 :(得分:6)

(我正在使用C ++ 11;我需要更新;))

15.1 / 3(强调我的):

  

throw-expression 初始化一个临时对象,称为异常对象,其类型通过从静态类型中删除任何顶级cv-quali firs 来确定throw的操作数

这意味着异常对象永远不会“生成const”,因此不应触发未定义的行为进行修改。

答案 1 :(得分:6)

来自[except.throw]:

  

使用操作数计算throw-expression会抛出异常(15.1);异常对象的类型   通过从操作数的静态类型中删除任何顶级cv限定符并调整   从“T的数组”或“返回T的函数”到“指向T的指针”或“指向函数返回T的指针”,   分别

并强调我的:

  

抛出异常copy-initializes(8.5,12.8)一个临时对象,称为异常对象。该   temporary是左值,用于初始化匹配处理程序(15.3)中声明的变量。

因此,如果我们抛出 cv T类型的操作数,我们将复制初始化类型为T的临时对象。

然后根据[except.handle],const T&amp;的处理程序。 (对于非指针类型T)匹配E类型的异常对象,如果:

  
      
  • [...] E和T属于同一类型(忽略顶级 cv-qualifiers ),
  •   
  • [...] T是E
  • 的明确公共基类   

此处理程序初始化为:

  

由异常声明声明的类型为 cv T或 cv T&amp;的变量从异常中初始化   E型物体,如下:
   - 如果T是E的基类,则变量从相应的基类子对象复制初始化(8.5)   异常对象;
   - 否则,变量从异常对象复制初始化(8.5)。

因此,如果我们通过const T&amp;捕获,我们将从异常对象中复制初始化引用 - 我们从上一节中知道它将是T类型或从T公开派生。来自[dcl.init .REF]:

  

对类型“ cv1 T1”的引用由“ cv2 T2”类型的表达式初始化,如下所示:
   - 如果引用是左值引用和初始化表达式
   - 是左值(但不是位字段),“ cv1 T1”与“ cv2 T2”引用兼容,或者[...] < / p>      

然后引用绑定到第一种情况下的初始化表达式lvalue

关键是临时异常对象仍然是左值。因此,如果我们的处理程序与const T&amp;匹配,我们知道引用直接绑定到类型为T或D的对象(其中D派生自T) - 无论哪种方式,它都是与{{引用 - 兼容的类型1}}。因此,没有未定义的行为。如果临时对象是rvalue或者处理程序可以匹配更广泛的类型,那么将创建类型为const T的临时对象 - 并且您的const T肯定是未定义的行为。

虽然您的代码在符合标准的编译器上没有显示未定义的行为,但实际上没有理由不这样做:

const_cast

答案 2 :(得分:3)

我看不到任何&#34;欺骗&#34;在标准中。无法证明是消极的,但我相信你已经安全了#34; const似乎在形式上与此相同:

T obj;
const T& t = obj;
const_cast<T &>(t).func();

也就是说,const首先出现在catch块中存在的引用中。{/ p>

但这一切都引出了一个问题:如果你不能通过观察来确定它,为什么要这样做呢?

肯定会抓住T&

答案 3 :(得分:0)

正如here所述:

  

如果catch子句的参数是引用类型,则对它做出的任何更改都会反映在异常对象中,如果使用throw重新抛出异常,则可以由另一个处理程序观察;

因此,这表明修改异常对象是安全的,并且可以观察到更改。

然而,人们应该考虑通过普通的引用来捕捉。或者,如果函数const仅适用于标记为mutable的成员,请将其设为segue

任何这些都不具有可读性,并且可能使共同开发者变得敌对。