0

这个问题是因为这个问题和评论

这个例子:

#include <iostream>

struct A {
    A(int value) : m_value(value) { }
    int m_value;
};

struct B : A {
    B(int value) : A (value) { }
};

int main()
{
    try {
        throw B(5);
    }
    catch(A) {
        std::cout << "A catch" << std::endl;
    }
    catch(B) {
        std::cout << "B catch" << std::endl;
    }
}

当使用 g++ 4.6.1 像这样编译时:

g++ exception_slicing.cpp -ansi -pedantic -Wall -Wextra

产生下一个输出:

exception_slicing.cpp: In function 'int main()':
exception_slicing.cpp:20:5: warning: exception of type 'B' will be caught [enabled by default]
exception_slicing.cpp:17:5: warning:    by earlier handler for 'A' [enabled by default]

输出是A catch

我了解由于切片问题而触发了第一个 catch 块。

  1. 它在哪里说基类中的隐藏复制构造函数?
  2. 它在哪里说明了这种行为?

PS1 请提供标准引用的答案。
PS2 而且我知道异常应该由 const 引用处理。

4

4 回答 4

3

在您的情况下,即使您通过 const 引用捕获,也会弹出相同的警告,而不会发生切片。您的问题是,因为B是==> 每个is-a的公共子类,所以它可以被第一个处理程序捕获。您可能应该从最具体到最不具体的顺序排列处理程序。ABA

此外,您正在"A"两个catch块中打印。

于 2011-11-03T07:57:35.593 回答
1

您给出的示例并没有真正演示切片,它只是警告您,由于 B 是 A,catch(A) 有效地隐藏了 catch(B)。

要查看切片的效果,您必须对 catch 中的 A 做一些事情:

catch(A a) {
    // Will always print class A, even when B is thrown.
    std::cout << typeid(a).name() << std::endl;
}
于 2011-11-03T08:06:21.673 回答
1

1 它在哪里说基类中的隐藏复制构造函数?

它不像隐含的那样隐藏。

使用n3290

§ 12 特殊成员函数

1/默认构造函数(12.1)、拷贝构造函数和拷贝赋值运算符(12.8)、移动构造函数和移动赋值运算符(12.8)、析构函数(12.4)是特殊的成员函数。[注意:当程序没有显式声明它们时,实现会为某些类类型隐式声明这些成员函数。如果它们被 odr-used (3.2),实现将隐式定义它们。见 12.1、12.4 和 12.8。——尾注]

所以,让我们跟随指针:

§ 12.8 复制和移动类对象

7/如果类定义没有显式声明复制构造函数,则隐式声明。[...]

8/类 X 的隐式声明的复制构造函数将具有以下形式

X::X(const X&)

if
— X 的每个直接或虚拟基类 B 都有一个复制构造函数,其第一个参数的类型为const B&or const volatile B&,并且
— 对于 X 的所有属于类类型 M(或其数组)的非静态数据成员,每个此类类类型有一个复制构造函数,其第一个参数是类型const M&或 const volatile M&

否则,隐式声明的复制构造函数将具有以下形式

X::X(X&)

你有它。A::A(A const&)在您的情况下,为您隐式定义了一个复制构造函数。


2 它在哪里说这种行为?

不出所料,在专门讨论异常处理的部分。

§ 15.3 处理异常

3/处理程序匹配 E 类型的异常对象,如果

[...]

— 处理程序的类型为 cvT或 cvT&并且T是 的明确公共基类E,或

[...]

它与函数中的参数传递非常相似。因为B公开继承自A,所以可以将 的实例B作为A const&. 由于复制构造函数不是显式的(嗯...),B可以转换为 a A,因此,对于函数, aB可以在A期望 a (无引用)的地方传递。

标准继续:

4/ try 块的处理程序按出现的顺序进行尝试。这使得编写永远无法执行的处理程序成为可能,例如通过将派生类的处理程序放在相应基类的处理程序之后。

这就是这个警告的真正意义所在。

于 2011-11-03T08:24:30.370 回答
0

它在哪里说基类中的隐藏复制构造函数?

该标准没有说明“隐藏的复制构造函数”。它确实说明了隐式定义的复制构造函数。

如果您必须有报价:

如果类定义没有显式声明复制构造函数,则没有用户声明的移动构造函数,一个是隐式声明的。

在 C++11 中,可以声明 thisdefaultdelete,具体取决于相关类的内容。我不会复制和粘贴一个类可能不可复制的完整原因列表。但我只想说,您的类将获得一个default复制构造函数。

它在哪里说明了这种行为?

关于什么行为?您看到的行为正是您在以下情况下所期望看到的:

void foo(A) {}

void main() {
  foo(B());
}

你得到切片,因为参数是按值取的。这需要一个副本。

异常处理程序不像函数调用;C++ 不会选择最接近的匹配项。它将选择第一个有效匹配。并且因为B A,所以匹配catch(A)

同样,如果您需要某种引用(尽管我不知道为什么;任何描述异常处理的基本 C++ 书籍都会告诉您这一点):

try 块的处理程序按出现的顺序进行尝试。这使得编写永远无法执行的处理程序成为可能,例如通过将派生类的处理程序放在相应基类的处理程序之后。

请注意,他们甚至给出了一个正是您的示例的示例。

而且我知道异常应该由 const 引用处理。

这就是为什么您通过引用来获取例外。

于 2011-11-03T08:16:11.293 回答