Sap*_*Sap 2 c++ constructor copy-constructor move-constructor c++11
我已经尝试过问这个问题,但我还不够清楚.所以这是另一个尝试.我对我的英语很抱歉;)
我们来看看代码:
#include <iostream>
#include <memory>
using namespace std;
struct A {
unique_ptr<int> ref;
void printRef() {
if (ref.get())
cout<<"i="<<*ref<<endl;
else
cout<<"i=NULL"<<endl;
}
A(const int i) : ref(new int(i)) {
cout<<"Constructor with ";
printRef();
}
~A() {
cout<<"Destructor with";
printRef();
}
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它无法编译,因为unique_ptr已删除复制构造函数.
奥利?
这个类有一个隐含的移动构造函数,因为unique_ptr有一个.
我们来做一个测试:
#include <iostream>
#include <memory>
using namespace std;
struct A {
unique_ptr<int> ref;
void printRef() {
if (ref.get())
cout<<"i="<<*ref<<endl;
else
cout<<"i=NULL"<<endl;
}
A(const int i) : ref(new int(i)) {
cout<<"Constructor with ";
printRef();
}
// Let's add a moving constructor.
A(A&& a) : ref(std::move(a.ref)) {
cout<<"Moving constructor with";
printRef();
}
~A() {
cout<<"Destructor with";
printRef();
}
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我添加了一个移动构造函数,现在代码可以编译和执行.
即使不使用移动构造函数.
输出:
构造函数为i = 0
构造函数,其中i = 1
析构函数,其中i = 1 析构函数,其中i
= 0
好的......
让我们再做一次测试并删除复制构造函数(但保留移动构造函数).
我没有发布代码,只添加了一行:
A(const A& a) = delete;
Run Code Online (Sandbox Code Playgroud)
你应该相信我 - 它有效.因此编译器不需要复制构造函数.但它确实做到了!(一个facepalm应该在这里)
所以发生了什么?我觉得这完全不合逻辑!还是有一些我看不到的扭曲逻辑?
再一次:
unique_ptr有一个移动的构造函数,并有一个删除的复制构造函数.编译器需要复制构造函数.但实际上编译器需要一个移动构造函数(即使它没有被使用)并且不需要复制(因为它可以被删除).正如我所看到的那样,移动的构造函数(应该是?)隐含地存在.
这有什么问题?
PS还有一件事 - 如果我删除移动构造函数,则程序无法编译.所以移动构造函数是必需的,但不是复制构造函数.
如果禁止在那里使用它,为什么需要copy-constructor ?
PPS非常感谢juanchopanza的回答!这可以通过以下方式解决:
A(A&& a) = default;
Run Code Online (Sandbox Code Playgroud)
还要非常感谢Matt McNabb.
正如我现在看到的那样,移动构造函数不存在,因为unique_ptr没有复制,类具有析构函数(一般规则是默认情况下,默认/复制/移动构造函数和析构函数只能一起生成).然后编译器不会停止移动一个(为什么?!)并回退到复制一个.此时,编译器无法生成它并在没有其他任何操作的情况下因错误(关于复制构造函数)而停止.
顺便说一句,你添加:
A(A&& a) = delete;
A(const A& a) = default;
Run Code Online (Sandbox Code Playgroud)
它无法编译时出现有关'A :: A(A && a)'删除的错误,复制构造函数不会回退.
PPPS最后一个问题 - 为什么它会在COPY构造函数中停止而不是MOVE构造函数?!
GCC++ 4.7/4.8说:" 错误:使用已删除的函数'A :: A(const A&)' "
因此它在复制构造函数处停止.
为什么?!应该有' A :: A(A &&) '
好.现在,这似乎是一个关于移动/复制建设者选择规则的问题.
我在这里创建了更具体的新问题
这称为复制省略.
在这种情况下,规则是指定了复制/移动操作,但是编译器被允许可选择的Elid它作为一种优化,即使复制/移动构造有副作用.
当发生复制省略时,通常直接在目的地的存储空间中创建对象; 而不是创建一个新对象,然后将其复制/移动到目标并删除第一个对象.
复制/移动构造函数仍然必须存在,否则我们最终会遇到代码似乎编译的愚蠢情况,但是当编译器决定不执行copy-elision时,则无法编译.或者代码可以在某些编译器上运行并在其他编译器上运行,或者如果您使用不同的编译器开关.
在第一个示例中,您不会声明副本或移动构造函数.这意味着它获得了一个隐式定义的拷贝构造函数.
然而,有一个规则,如果一个类有一个用户定义的析构函数,然后它并没有得到一个隐式定义的移动构造函数.不要问我为什么存在这个规则,但它确实存在(参见[class.copy]#9作为参考).
现在,标准的确切措辞在这里很重要.在[class.copy]#13中它说:
如果使用了odr-used(3.2),则默认定义一个默认且未定义为已删除的复制/移动构造函数
[注意:即使实现省略了odr-use(3.2,12.2),也会隐式定义复制/移动构造函数. - 注意
odr-used的定义非常复杂,但它的要点是,如果你从不尝试复制对象,那么它将不会尝试生成隐式定义的复制构造函数(同样也用于移动和移动).
正如TC在您之前的线程中解释的那样,做的操作A a[2] = {0, 1};
确实指定了复制/移动,即a[0]
必须通过复制或移动来临时初始化值A(0)
.这个临时版本可以进行复制省略,但正如我之前解释的那样,正确的构造函数必须仍然存在,以便在这种情况下编译器决定不使用复制省略时代码可以工作.
由于您的类在此处没有移动构造函数,因此无法移动它.但到临时绑定的构造函数中的尝试A
仍然成功,因为是定义(尽管隐式定义的)一个拷贝构造函数.此时,odr-use会发生并且它会尝试生成复制构造函数并因此而失败unique_ptr
.
在第二个示例中,您提供了一个move-constructor但没有copy-constructor.仍有一个隐式声明的复制构造函数,它在被使用之前不会被生成,就像之前一样.
但是重载决策的规则说如果副本和移动都是可能的,那么使用移动构造函数.所以在这种情况下它并没有使用复制构造函数,一切都很好.
在第三个示例中,move-constructor再次赢得重载决策,因此无论copy-constructor的定义方式如何.
归档时间: |
|
查看次数: |
223 次 |
最近记录: |