首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >这个奇怪的复制构造函数错误抱怨什么?

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

Stack Overflow用户
提问于 2017-10-14 11:15:55
回答 2查看 3.2K关注 0票数 15

我上的是2017。最近,由于我不喜欢C++的不符合标准,所以我继续在选项中禁用了非标准语言扩展。到目前一切尚好。现在我有麻烦了。

代码语言:javascript
运行
复制
#include <iostream>
#include <vector>


struct Vertex
{
    Vertex(float pos) { }
    Vertex(Vertex& other) { }
};

std::vector<Vertex> arrayOfVertices;

int main()
{
    arrayOfVertices.emplace_back(7.f);
}

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

“编译器中发生了内部错误”

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

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

错误:从‘顶点’类型的r值中初始化“顶点&”类型的非const引用无效

我认为复制构造函数不必是const,在我的例子中,我想修改另一个引用中的一些内容。我知道非const参数不能接受r值引用,但是我对它进行了测试,结果发现在vector::emplace_back()中根本没有调用复制构造函数:

代码语言:javascript
运行
复制
#include <iostream>
#include <vector>

struct Vertex
{
    Vertex(float pos) 
    { 
        std::cout << "Calling constructor\n";
    }
    Vertex(const Vertex& other) 
    { 
        std::cout << "Calling copy constructor\n";
    }
};

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

}

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

EN

回答 2

Stack Overflow用户

发布于 2017-10-14 11:47:15

问题是您没有移动构造函数。

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

您的类有一个用户定义的复制构造函数,因此移动构造函数被隐式删除。因此,试图将原始缓冲区中的任何元素移动到新的,将变成试图复制,通过过载解析。你把注意力放在新的位置上,实际上是一条红鲱鱼,在这个简单的例子中,真正的问题是显而易见的:

代码语言:javascript
运行
复制
Vertex v1{7.f},
       v2{std::move(v1)};
       // Error, the xvalue from `move` can't bind to a non-const reference

例如,通过显式地默认移动构造函数,您可以很容易地使错误保持沉默:

代码语言:javascript
运行
复制
struct Vertex
{
    Vertex(float) 
    { 
        std::cout << "Calling constructor\n";
    }

    Vertex(Vertex&&) = default;

    Vertex(Vertex&) 
    { 
        std::cout << "Calling copy constructor\n";
    }
};

永远不要忘记,在C++11中,0/3的规则变成了0/3/5的规则。

票数 17
EN

Stack Overflow用户

发布于 2017-10-14 11:44:46

显然,如果编译器给出内部错误,这就是编译器的错误。

emplace_back(7.f)使用构造函数Vertex(float pos)嵌入对象--没有直接涉及复制构造函数。

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

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

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

在不涉及太多细节的情况下,非const复制构造函数和没有移动构造函数的组合意味着对象失败了MoveInsertable需求,因为在上述需求中的rvalue参数不会绑定到非const值引用。

票数 13
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46743898

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档