首页 > 解决方案 > 尝试通过引用将 const 成员传递给类时引用已删除的函数

问题描述

这是对在通过 const 成员通过引用传递时尝试引用已删除函数的问题的一种扩展

我读到了,我确实理解了有关 MoveConstructible 的解释(或者至少,我相信),但鉴于以下代码,我无法弄清楚原因:

struct vertex {
    const double x;
    const double y;
    const double z;
};

const std::vector<vertex> test_vertices()
{
    std::vector<vertex> vertices;

    vertices.emplace_back(vertex(-3.0, -3.0, 0.0));
    vertices.emplace_back(vertex(3.0, -3.0, 0.0));
    vertices.emplace_back(vertex(0.0, 0.0, 1.5));
}

这段代码完美无缺:

std::vector<vertex> panel_vertices = test_vertices();
draw(panel_vertices);

但此其他代码没有并返回“C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.23.28105\include\xutility(1762,1): error C2280: ' p_geometry::vertex &p_geometry::vertex::operator =(const p_geometry::vertex &)':试图引用已删除的函数”(忽略命名空间,它们在代码中是可以的):

class PanelView {

    std::vector<vertex> vertices;
public:

    PanelView(int x, int y, int w, int h, const char* l = 0);
    void setVertices(const std::vector<vertex>& vertices): vertices(vertices) {};
    void draw() {
        other_draw(vertices);
    };
};

我这里没有排序运算符,但除此之外,我还遗漏了一些东西,因为两个代码片段似乎都在做同样的事情。

更新

在您回复后(谢谢!!)我试图澄清我的想法和怀疑。我尝试了几种配置,并且我可以在有 const 成员时删除复制运算符(恕我直言,这是一个非常愚蠢的行为,因为当您创建副本时,您并没有尝试修改原始文件中的任何内容,但这不在这个问题的范围)。

我得出以下四种情况 [1, 2, 2bis, 3]:

#include <iostream>

struct vertex {
    const double x;
    const double y;
    const double z;
};

const std::vector<vertex> test_vertices()
{
    std::vector<vertex> vertices;
    vertices.push_back(vertex {-3.0, -3.0, 0.0});
    vertices.emplace_back(vertex{3.0, -3.0, 0.0});
    vertices.emplace_back(vertex{0.0, 0.0, 1.5});
    return vertices;
}

class PanelView {
    std::vector<vertex> mvertices;
    std::vector<vertex> *mvpointer;
public:

    //[1] PanelView(const std::vector<vertex>& vertices): mvertices(vertices) {};
    //[2] void setVertices(const std::vector<vertex>& v)  {mvertices = v;  };
    //[2 bis] void setVertices(std::vector<vertex> v)  {mvertices = v;  };
    //[3] void setVerticesPointer(std::vector<vertex> *v)  {mvpointer = v;  };
    void draw() {std::cout<<mvertices[0].x; };
    void drawp() {std::cout<<(*mvpointer)[0].x; };
};


int main()
{

    std::vector<vertex> panel_vertices = test_vertices();

    //[1] Works fine
    //PanelView pv(panel_vertices);
    //pv.draw();

    //[2 and 2 bis] - Error C2280: 'vertex &vertex::operator =(const vertex &)': deleted
    //PanelView pv;
    //pv.setVertices(panel_vertices);

    //[3] - OK
    PanelView pv;
    pv.setVerticesPointer(&panel_vertices);
    pv.drawp();


    std::cout<<panel_vertices[0].x;
}

PanelView 构造函数 [1] 和 setVertices 方法 [2 和 2 bis] 执行相同的操作,将“const std::vector& vertices”作为输入并将其(或什么?)复制到类属性“std::vector顶点;"。

第一个疑问:为什么使用 setVertices 方法会失败,而使用构造函数却不会?“幕后”有什么区别?

第二个疑惑:我一直认为在 C++ 中通过引用传递和通过指针传递基本相同,但是通过引用传递更多的是 C++ 风格,并且避免了所有样板代码,使其更具可读性。为什么编译器抱怨 [2 (by reference)],但对 [3 with pointers] 没问题?

标签: c++

解决方案


简单的答案是,在您的示例中 operator= - 被删除 - 没有被调用,这就是它起作用的原因。对比:

setVertices(const std::vector<vertex>& v)

接受一个现有的向量并将每个值一个接一个地复制vertices到一个新的向量中。好吧,至少这是编译器尝试生成的代码,类似于:

vertices.reserve(v.size);
for (size_t i = 0; i < v.size(); ++i) {
 vertices[i] = v[i];
}

请注意,上面的代码期望operator=分配一个vertices[i] = v[i]; 另外,注意:) 这不是 STL 将生成的实际代码 - 它仅用于说明operator=预期的位置。实际代码最有可能使用通用复制算法和迭代器。

至于您的示例-在调用复制构造函数时vertices.emplace_back(vertex(0.0, 0.0, 1.5));不需要operator=请求-我相信它是默认生成的。复制构造函数通过放置 new 调用。


推荐阅读