这个奇怪的复制构造函数错误抱怨的是什么

What is this strange copy constructor error complaining about?

本文关键字:错误 是什么 构造函数 复制      更新时间:2023-10-16

我正在使用Visual Studio 2017。最近,因为我不喜欢C++的不一致标准,我继续禁用了选项中的非标准语言扩展。到目前为止还不错。现在我有个问题。

#include <iostream>
#include <vector>

struct Vertex
{
Vertex(float pos) { }
Vertex(Vertex& other) { }
};
std::vector<Vertex> arrayOfVertices;
int main()
{
arrayOfVertices.emplace_back(7.f);
}

这不会在Visual Studio中编译,它给出的唯一错误是:

"编译器中发生内部错误">

如果我启用语言扩展,它编译得很好。如果我禁用语言扩展,并使复制构造函数采用const Vertex&,它编译得很好。

因此,我在一些在线编译器上尝试了GCC,如果复制构造函数不采用const引用参数,它就不会编译,会出现各种错误。似乎最有意义的是:

错误:"Vertex&"类型的非常量引用的初始化无效来自"顶点"类型的右值

我认为复制构造函数不必是const,在我的情况下,我想修改其他引用中的一些内容。我知道非常量参数不能接受r值引用,但我测试了它,结果发现在vector::emplace_back()中根本没有调用复制构造函数:

#include <iostream>
#include <vector>
struct Vertex
{
Vertex(float pos) 
{ 
std::cout << "Calling constructorn";
}
Vertex(const Vertex& other) 
{ 
std::cout << "Calling copy constructorn";
}
};
std::vector<Vertex> arrayOfVertices;
int main()
{
arrayOfVertices.emplace_back(7.f); // Normal constructor called if const,
// doesn't compile if non-const
auto buff = malloc(sizeof(Vertex)); // Placement new
new (buff) Vertex(7.f); // Normal constructor called whether const 
// or non-const. This is what I thought emplace_back did
}

所以我不知道发生了什么。我想首先知道如果没有调用复制构造函数,为什么会发生这种情况,以及在这种情况下,是否有一种方法可以在我的复制构造函数中使用非常量,即使用vector::emplace_back(),因为这个问题似乎只在使用vector::emplace_back()时出现。

问题是您没有move构造函数。

当您请求std::vectoremplace_back时,它必须确保有足够的存储空间来构造新对象。这个例程的一部分是实例化一堆代码,将元素从旧缓冲区移动到任何新分配的缓冲区(如果需要的话)。即使在运行时没有重新分配,该代码也将由模板实例化。

您的类有一个用户定义的复制构造函数,因此move构造函数被隐式删除。因此,通过过载解析,将原始缓冲区中的任何元素移动到新缓冲区的尝试将转变为复制尝试。你对新职位的关注实际上是转移注意力,真正的问题在这个简单的例子中很明显:

Vertex v1{7.f},
v2{std::move(v1)};
// Error, the xvalue from `move` can't bind to a non-const reference

您可以通过使move构造函数返回来很容易地消除错误,例如,通过显式默认它:

struct Vertex
{
Vertex(float) 
{ 
std::cout << "Calling constructorn";
}
Vertex(Vertex&&) = default;
Vertex(Vertex&) 
{ 
std::cout << "Calling copy constructorn";
}
};

永远不要忘记,在C++11中,0/3的规则变成了0/3/5的规则。也要仔细考虑类的移动语义。

显然,如果编译器出现内部错误,那就是编译器错误。

emplace_back(7.f)使用构造函数Vertex(float pos)来设置对象的模板——复制构造函数不直接涉及。

错误的实际原因不同。通常情况下,当您在向量中放置时,可能会发生重新分配。如果是,则必须将向量中的所有对象重新定位到内存中的新位置。

显然,是否发生重新分配是一个运行时条件。在运行时出现编译错误是不可行的;因此如果对象不支持重新分配,则在编译时使用CCD_;即使这个调用的向量恰好是空的。

标准术语可在C++14表87中找到:为了放置到向量中,元素类型必须是MoveInsertable和MoveAssignable。

不需要过多的细节,非常量复制构造函数和无移动构造函数的组合意味着对象无法满足MoveInsertable要求,因为所述要求中的右值参数将不会绑定到非常量左值引用。