【问题标题】:C++ throwing class membersC++ 抛出类成员
【发布时间】:2015-10-20 16:30:37
【问题描述】:

我有以下 C++ 代码

template <class E>
class ExceptionWrapper {
public:
    explicit ExceptionWrapper(const E& e): e(e) {}

    void throwException() {
        throw e;
    }

private:
    E e;
};

...
try {
    ExceptionWrapper<E> w(...);
    w.throwException();
} catch (const E& e) {
    ...
}
...

问题:此代码有效吗?我可以争辩说,返回对类成员的引用几乎总是无效的(我相信每个人都同意这个说法)。但是,我的同事声称throw 并非如此。

附:在将 catch (const E&amp; e) 更改为 catch (E e) 后,一个讨厌的错误似乎消失了,这加强了我的立场 - 此代码无效有效。

【问题讨论】:

  • 上面的代码应该是有效的,并且通过引用捕获应该可以工作。 a)您的真实代码与上述不同吗? b)您使用的是哪个编译器? throwing e 应该抛出 e 的副本。
  • 一个真实的例子会很好。示例中缺少/错误太多。
  • @user2079303 是的。对不起,我的错,我做了一个编辑。
  • "我可以说返回对类成员的引用几乎总是无效的(我相信每个人都同意这个说法)。" 我会这么说大多数吸气剂都这样做。它们返回对成员的 const 引用。
  • 您的同事声称throw 的情况并非如此?没有语法可以通过引用抛出任何东西(除非用throw; 重新抛出)。

标签: c++ exception reference


【解决方案1】:

我的主张是,通过引用捕获 e 是无效的,因为 e 是 w 的成员,而 w 在 catch 范围内不存在。

您的声明不正确。 throw e; 抛出成员的副本,并且该副本在 catch 的范围内有效。

§ 15.1 / 3(n3797 草案):

抛出异常复制初始化 ( 8.5 , 12.8 ) 一个临时对象,称为 异常对象 .这 临时是一个左值,用于初始化匹配中命名的变量 处理程序 ( 15.3 )。如果 异常对象的类型将是不完整类型或指向不完整类型的指针 (可能是 cv 合格的) 空白 该程序格式不正确。评估一个 抛出表达式 带有操作数 throws 一个例外;异常对象的类型是通过删除任何顶层来确定的 cv 限定词 来自 操作数的静态类型并从“array of 吨 ”或“函数返回 吨 ” 到 “指向 吨 ” 或 “指向函数返回的指针 吨 ,”分别。

通过 const 引用捕获是捕获异常的首选方法。它允许捕获std::exception 的派生词,而无需对异常对象进行切片。

【讨论】:

  • 没错。而且您应该始终通过引用来捕获以避免第二次复制。
  • 对任何确认您立场的 C++ 标准的任何引用都足以让我接受您的回答并感谢您!
  • @Hindsight 快速浏览一下 cppreference 中throw 中的解释。 First, copy-initializes the exception object from expression。有时间我去看看标准。
  • @Hindsight 你去,标准引用。
  • 您应该指定通过 const 引用捕获是首选方式
【解决方案2】:

我认为相关点是:

15.1。 抛出异常:

p3. throw-expression 初始化一个临时对象,称为异常对象,其类型已确定 通过从 throw 操作数的静态类型中删除任何顶级 cv 限定符并调整类型 从“T 的数组”或“返回 T 的函数”分别到“指向 T 的指针”或“指向返回 T 的函数的指针”。 临时值是一个左值,用于初始化匹配处理程序 (15.3) 中命名的变量。 如果 异常对象的类型将是不完整类型或指向不完整类型的指针 other 比(可能是 cv-qualified) void 程序格式错误。除了这些限制和限制 在 15.3 中提到的类型匹配中,throw 的操作数被完全视为 a 中的函数参数 调用 (5.2.2) 或返回语句的操作数。

这是来自draft for c++11,重点是我的。

这基本上意味着有一个从throw 的参数创建的临时对象。就像有一个函数E f(){return private_e;} 一样,该临时函数用作适当处理程序的参数。因此,如果您没有通过引用捕获,实际上您将有两个可能的副本。

可能也相关:

p5. 当抛出的对象是类对象时,复制/移动构造函数和析构函数应该是可访问的, 即使省略了复制/移动操作 (12.8)。

【讨论】:

    【解决方案3】:

    如果构造函数失败/抛出,w 不存在。所以“w.throwException()”是无效的。

    【讨论】:

    • 我不明白你的意思。构造函数不会失败/抛出。请将此代码视为学术示例。我不希望 cmets 处理与问题中描述的问题没有直接关系的任何事情。
    • 显示的构造函数不会抛出,即使抛出,w.throwException(); 行也不会到达。
    猜你喜欢
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 2013-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多