什么是隐藏切片复制构造函数
What is hidden slicing copy constructor?
这个问题是因为这个问题和评论。
这个例子:
#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块是由于切片问题而触发的。
- 它在哪里说隐藏的复制构造函数在基类?
- 它在哪里说这种行为?
PS1请以标准报价提供答案。
我知道异常应该由const引用处理。
在您的例子中,即使通过const引用捕获,也会弹出相同的警告,而没有发生切片。您的问题是,由于B
是A
==>的公共子类,每个B
都是一个A
,因此它可以被第一个处理程序捕获。您可能应该按照最具体到最不具体的顺序排列处理程序。
另外,您在两个catch
块中打印"A"
。
1基类中隐藏的复制构造函数在哪里?
与其说它是隐藏的,不如说是隐含的。
使用n3290
:
§12特殊成员函数
1/默认构造函数(12.1)、复制构造函数和复制赋值操作符(12.8)、移动构造函数和移动赋值操作符(12.8)以及析构函数(12.4)都是特殊的成员函数。[注意:当程序没有显式声明这些成员函数时,实现将隐式声明某些类类型的成员函数。]如果它们被频繁使用,实现将隐式地定义它们(3.2)。参见12.1、12.4和12.8。-end note]
那么,让我们跟随指针:
§12.8复制和移动类对象
7/如果类定义没有显式声明复制构造函数,则隐式声明复制构造函数。[…]
8/类X隐式声明的复制构造函数的形式为
X::X(const X&)
如果
否则,隐式声明的复制构造函数的形式为
X的每个直接基类或虚基类B都有一个复制构造函数,其第一个形参类型为const B&
或const volatile B&
,并且
对于类类型为M的X的所有非静态数据成员(或其数组),每个此类类类型都有一个复制构造函数,其第一个参数类型为const M&
或const volatileM&
。
X::X(X&)
就是这样了。在您的例子中,有一个为您隐式定义的复制构造函数A::A(A const&)
。
2关于这种行为它说了什么?
不出所料,在专门讨论异常处理的部分。
§15.3异常处理
3/处理程序匹配E类型的异常对象,如果
[…]
-处理程序的类型是cv
T
或cvT&
,T
是E
的一个明确的公共基类,或[…]
这与函数中的参数传递非常相似。因为B
公开继承A
,所以B
的实例可以作为A const&
传递。由于复制构造函数不是显式的(哼…),B
可以转换为A
,因此,对于函数来说,在需要A
(不引用)的地方可以传递B
。
4/ try块的处理程序按照出现的顺序进行尝试。这使得编写永远无法执行的处理程序成为可能,例如,将派生类的处理程序放在相应基类的处理程序之后。
这就是这个警告的意义所在。
您给出的示例并没有真正演示切片,它只是警告您,由于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;
}
基类中隐藏的复制构造函数在哪里?
标准没有提到"隐藏复制构造函数"。它确实说了一些关于隐式定义的复制构造函数的东西。
如果你必须有引号:
如果类定义没有显式声明复制构造函数,则没有用户声明的move构造函数,而是隐式声明的。
在c++ 11中,它可以声明为default
或delete
,这取决于所讨论的类的内容。我不打算复制粘贴一个类不可复制的完整原因列表。但只需说明,您的类将获得一个default
复制构造函数。
关于这种行为它说了什么?
关于什么行为?您看到的行为正是您期望在以下情况下看到的:
void foo(A) {}
void main() {
foo(B());
}
得到切片,因为参数是按值取的。需要一个副本。
异常处理程序不像函数调用;c++不会选择最接近的匹配。它将选择第一个有效匹配。由于B
是 A
,因此它匹配catch(A)
。
再次强调,如果你需要一些引用(虽然我不知道为什么;任何描述异常处理的c++基础书籍都会告诉你):
try块的处理程序按照出现的顺序进行尝试。这使得编写永远无法执行的处理程序成为可能,例如,将派生类的处理程序放在相应基类的处理程序之后。
请注意,他们甚至给出了一个与你的例子完全相同的例子。
并且我知道异常应该由const引用来处理。
这就是为什么通过引用接受异常
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 当从函数参数中的临时值调用复制构造函数时
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 为什么类中的ostringstream类型的成员会导致";调用隐含删除复制构造函数";错误
- 复制构造函数、赋值运算符C++
- std::ofstream 作为类成员删除复制构造函数?
- 复制构造函数C++无法正确复制指针
- 关于复制构造函数的一个棘手问题
- 为什么调用复制构造函数而不是移动构造函数?
- 填充上编译器生成的复制构造函数之间的不一致
- C++ 对象指针数组的复制构造函数
- C++ 基本 CTOR 说明 - 为什么不调用赋值/复制构造函数
- 防止在复制构造函数中隐式调用基构造函数
- 为用户定义的类正确调用复制构造函数/赋值运算符
- 具有已删除移动和复制构造函数的类的就地构造
- 复制构造函数隐式转换问题
- 复制构造函数中的递归调用