这个问题是因为this question and the comments。
这个例子:
#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
。
我理解由于the slicing problem而触发了第一个catch块。
PS1请提供标准引用的答案 PS2我知道异常应该由const引用处理。
答案 0 :(得分:3)
在您的情况下,即使您通过const引用捕获,也会弹出相同的警告,其中不会发生切片。您的问题是B
是A
==&gt;的公共子类。每个B
都是一个A
,因此它可以被第一个处理程序捕获。您应该订购从最具体到最不具体的处理程序。
此外,您在两个"A"
块中打印catch
。
答案 1 :(得分:1)
你给出的例子并没有真正展示切片,它只是警告你,因为B是-A A,所以catch(A)有效地隐藏了捕获(B)。
要查看切片的效果,您必须对catch中的A执行某些操作:
catch(A a) {
// Will always print class A, even when B is thrown.
std::cout << typeid(a).name() << std::endl;
}
答案 2 :(得分:1)
1关于基类中隐藏的拷贝构造函数的说法在哪里?
隐藏的内容并不隐藏。
使用n3290
:
§12特别会员职能
1 / 默认构造函数(12.1),复制构造函数和复制赋值运算符(12.8),移动构造函数和移动赋值运算符(12.8)以及析构函数(12.4)是特殊成员函数。 [注意:当程序没有显式声明它们时,实现将隐式声明某些类类型的这些成员函数。如果它们使用得很多,那么实现将隐式定义它们(3.2)。见12.1,12.4和12.8。 - 后注]
所以,让我们按照指针:
§12.8复制和移动类对象
7 / 如果类定义没有显式声明复制构造函数,则会隐式声明一个。 [...]
8 / 类X的隐式声明的复制构造函数将具有
形式
X::X(const X&)
如果
- X的每个直接或虚拟基类B都有一个复制构造函数,其第一个参数类型为const B&
或const volatile B&
,并且 - 对于类型为M(或其数组)的X的所有非静态数据成员,每个此类类型都有一个复制构造函数,其第一个参数的类型为const M&
或const volatile {{1} }。否则,隐式声明的复制构造函数将具有
形式
M&
你有它。在您的情况下,有一个隐式为您定义的复制构造函数X::X(X&)
。
2它对这种行为有什么看法?
毫无疑问,在致力于异常处理的部分。
§15.3处理例外
3 / 处理程序是E类型的异常对象的匹配,如果
[...]
- 处理程序的类型为cv
A::A(A const&)
或cvT
,而T&
是明确的T
公共基类,或[...]
它与函数中的参数传递非常相似。由于E
公开继承自B
,因此A
的实例可以作为B
传递。由于复制构造函数不是显式的(hum ...),A const&
可以转换为B
,因此,对于函数,A
可以在{{1}处传递(预期没有参考)。
标准继续:
4 / 尝试块的处理程序按外观顺序进行尝试。这使得编写永远不会执行的处理程序成为可能,例如通过在相应基类的处理程序之后放置派生类的处理程序。
这是警告的真正含义。
答案 3 :(得分:0)
它在基类中的隐藏拷贝构造函数中说了什么?
该标准没有提到“隐藏的拷贝构造函数”。它确实说明了一个隐式定义的复制构造函数。
如果你必须有一个引用:
如果类定义没有显式声明复制构造函数,则没有用户声明的移动构造函数,其中一个是隐式声明的。
在C ++ 11中,可以声明default
或delete
,具体取决于相关类的内容。我不打算复制并粘贴为什么类可能无法复制的完整列表。但足以说明,您的课程将获得default
副本构造函数。
它对这种行为有什么看法?
关于什么行为?您看到的行为正是您希望在以下情况中看到的行为:
void foo(A) {}
void main() {
foo(B());
}
你得到切片,因为参数是按值获取的。这需要一份副本。
异常处理程序与函数调用不同; C ++不会选择最接近的匹配。它将选择第一个有效匹配。由于B
是 A
,因此它与catch(A)
匹配。
同样,如果你需要某种引用(虽然我不知道为什么;任何描述异常处理的基本C ++书都会告诉你这个):
尝试块的处理程序按外观顺序进行尝试。这使得编写永远不会执行的处理程序成为可能,例如通过在相应基类的处理程序之后放置派生类的处理程序。
请注意,他们甚至给出了一个完全符合您榜样的例子。
我知道异常应该由const引用来处理。
这是为什么你通过引用获取异常。