C++ 通过常量引用捕获异常并丢弃常量

C++ 通过常量引用捕获异常并丢弃常量,c++,exception,language-lawyer,C++,Exception,Language Lawyer,当T是具有非const成员函数func的某个类时,这是否作为异常处理程序有效 换句话说:catch是否保证直接绑定到(可修改的)异常对象,或者当您通过const引用捕获时,编译器是否有足够的空间进行一些欺骗 catch(const T &t) { const_cast<T &>(t).func(); } catch(常数T&T) { const_cast(t.func(); } 我看不到标准中有任何“欺骗”的迹象。不能证明是否定的,但我相信你是“安全的”。常

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

换句话说:
catch
是否保证直接绑定到(可修改的)异常对象,或者当您通过const引用捕获时,编译器是否有足够的空间进行一些欺骗

catch(const T &t)
{
    const_cast<T &>(t).func();
}
catch(常数T&T)
{
const_cast(t.func();
}
我看不到标准中有任何“欺骗”的迹象。不能证明是否定的,但我相信你是“安全的”。
常数
在形式上与此等效:

T obj;
const T& t = obj;
const_cast<T &>(t).func();
tobj;
常数T&T=obj;
const_cast(t.func();
也就是说,
const
ness首先出现在
catch
块中存在的引用上,就是这样

但这一切确实回避了一个问题:如果你不能通过看它来确定,为什么要这么做

当然,只要抓住一个
T&

就像上面说的:

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

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

然而,人们应该只考虑一个普通的参考。或者,如果函数只能在标记为
可变的成员上工作,则将其设置为
常量

所有这些听起来都不可读,会让共同开发人员产生敌意。

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

15.1/3(重点矿山):

throw表达式初始化临时对象,称为异常对象,其类型通过从throw操作数的静态类型中删除任何顶级cv限定符来确定

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

来自[except.throw]:

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

我的重点是:

抛出异常副本会初始化(8.5,12.8)一个临时对象,称为异常对象。这个 temporary是一个左值,用于初始化匹配处理程序(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) 例外对象的定义
-否则,将从exception对象复制初始化变量(8.5)

因此,如果我们通过const T&捕获,我们将从exception对象复制初始化引用-我们从上一节中知道,exception对象的类型将是T,或者是从T公开派生的。来源于[dcl.init.ref]:

对“cv1 T1”类型的引用由“cv2 T2”类型的表达式初始化,如下所示:
-如果引用是左值引用且初始值设定项表达式
-是左值(但不是位字段),“cv1 T1”是与“cv2 T2”兼容的引用,或[…]

然后,在第一种情况下,引用被绑定到初始值设定项表达式lvalue

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

虽然您的代码在一致性编译器上没有未定义的行为,但实际上没有理由不这样做:

catch(T &t)
{
    t.func();
}

因为Matt显然是一种语言lawyer@dufresnb:这绝对不是编写这样的代码的理由。@LightnessRacesinOrbit,这样我就可以对编写此代码的其他人做出明确的响应code@MattMcNabb:哈哈,那好吧。还是。答案是“你很安全,但很愚蠢”,所以只要自信地回答就行了。不要真正回答这个问题。这意味着如果你通过引用和重播,重播的处理者可以看到你的变化。这并不意味着修改
const&
的任何内容都可以。@LightnessRacesinOrbit问题是“捕获是否保证直接绑定到(可修改的)异常对象”。引用表明它是。@BillyONeal
const T&
是一种引用类型,因此该引用似乎是相关的,即使不是直接引用。@BartoszKP:哦,我想是这样的。如果你在答案中加上这个结论会更清楚;nice.FWIW在C++14中的措辞是相同的,从技术上讲,这并不能证明ref绑定到上述定义的对象,也没有“欺骗”,但是,正如在我自己的回答中,你不能反驳否定。为什么会这样