我正在尝试使用GCC 4.7.2(MinGW)编译以下简单代码。这里我使用的是C ++ 11特性 - 非静态成员初始化器:
#include <iostream>
using namespace std;
struct A
{
int var;
A()
{
cout << "A()\n";
}
A(int i)
{
cout << "A(int i)\n";
var = i;
}
A(const A&) = delete;
};
struct B
{
A a = 7;
};
int main()
{
B b;
cout << "b.a.var = " << b.a.var;
return 0;
}
此代码无法编译,因为删除了复制构造函数,这里不需要。这是错误:
main.cpp:27:11: error: use of deleted function 'A::A(const A&)'
main.cpp:13:5: error: declared here
main.cpp: In constructor 'constexpr B::B()':
main.cpp:25:8: error: use of deleted function 'A::A(const A&)'
main.cpp:13:5: error: declared here
如果我实现这样的复制构造函数:
A(const A& a)
{
cout << "A(const A&)\n";
var = a.var;
}
然后代码编译好,程序给我预期的输出:
A(int i)
b.a.var = 7
所以这意味着没有使用复制构造函数,但为什么我不能删除它?
修改:谢谢您的回答。如果我使用=
,则标准需要复制或移动构造函数。要解决此问题,我需要实现移动构造函数或使用直接初始化语法A a{7}
。
答案 0 :(得分:4)
a
的初始化程序为您提供了复制初始化:
A a = 7;
对于这种需要用户定义转换的复制初始化,结果初始化等同于:
A a(A(7));
即构造临时A
,然后传递给a
对象的复制构造函数。可以省略此复制,但复制构造函数必须可用。换句话说,如果首先可以复制,则只能省略复制。如果您delete
复制构造函数,则无法复制。
如果您执行以下操作,您将有更好的时间使用已删除的复制构造函数:
A a{7};
这是直接初始化,不需要复制构造函数。
答案 1 :(得分:3)
允许复制初始化删除副本,但标准可以访问复制构造函数。
答案 2 :(得分:2)
根据C ++ 11标准的第12.2 / 14段:
以
形式出现的初始化T x = a;
以及参数传递,函数返回,抛出异常(15.1),处理异常(15.3)和聚合成员初始化(8.5.1)称为复制初始化。 [注意:复制初始化可以调用移动(12.8)。 - 注意]
复制初始化无法编译的原因是在复制初始化期间需要创建临时对象(至少在逻辑上),并且要从中构造初始化对象
现在所有以前的答案似乎只关注复制构造函数,但这里的第一个问题是缺少 move-constructor 。只要你提供一个,那么复制构造函数就不是必需的。
唉,删除复制构造函数会阻止生成隐式移动构造函数。明确添加一个可以解决问题:
struct A
{
int var;
A()
{
cout << "A()\n";
}
A(int i)
{
cout << "A(int i)\n";
var = i;
}
A(const A&) = delete;
// THIS MAKES IT WORK
A(A&& a)
{
cout << "A(A&&)\n`;
var = a.var;
}
};
请注意,当move-constructor和copy-constructor都存在时,首选
当移动构造函数不存在时,编译器可以调用复制构造函数来执行初始化,因为常量左值引用可以绑定到右值引用,并且复制被视为未优化的移动。
但是,即使编译器允许忽略对move或复制构造函数的调用,仍必须检查操作的语义。根据C ++ 11标准的第12.8 / 32段:
当满足或将满足复制操作的省略标准时,除了源对象是函数参数,并且要复制的对象由左值指定,重载决策以选择构造函数首先执行复制,就好像对象是由右值指定的一样。如果重载决策失败,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值。 [注意:无论是否发生复制省略,都必须执行此两阶段重载决策。如果不执行elision,它将确定要调用的构造函数,并且即使调用被省略,也必须可以访问所选的构造函数。 -end note] [...]
因此,如果移动构造函数和复制构造函数都不存在,编译器将发出错误。
但是,如果您愿意,可以直接初始化您的对象,而不是复制初始化它。只需使用直接初始化语法:
struct B
{
A a{7};
};
这将使move-constructor和copy-constructor变得不必要,因为直接初始化对象时不会创建临时。
答案 3 :(得分:1)
所以这意味着没有使用复制构造函数,但为什么我不能删除它?
在您的情况下,复制构造函数已使用仅适用于标准要求的语义检查,它还需要可访问。稍后,编译器会优化代码,忽略对复制构造函数的调用,因此实际上并没有调用。
答案 4 :(得分:1)
此代码无法编译,因为此处不需要删除的复制构造函数
抱歉,您的复制构造函数是是必要的。尽管可以优化副本,但仍然可以在代码中实现。这是由语言强制执行的。