c++vectorreferencegetterconst-correctness

Const-correctness with getters of vector of non-pointers


I have a quick question regarding const-correctness for getters of vector of non-pointers.

Basically, I have this class with a non-const getter and a const getter for m_Vertices:

class Mesh
{
public:
    std::vector<Vertex>& GetVertices() { return m_Vertices; }

    const std::vector<Vertex>& GetVertices() const { return m_Vertices; }

private:
    std::vector<Vertex> m_Vertices;
}

The non-const getter makes sense; the getter is non-const, we return a non-const reference to a vector of non-const Vertex instances.

The const getter in the other hand doesn't makes sense to me. I'm questioning the const-correctness and whether I should just return a copy of the vector. The problem I see is that we return a const reference to a vector of non-const Vertex instances. So, the callee will have a const reference to m_Vertices, but will still be able to modify the Vertex instances, which feels wrong to me in terms of const-correctness.

In that case, should I simply return a copy of m_Vertices?


Solution

  • First of all, understand that this is an opinion-based question. There are no right or wrong answers when you are designing a class regarding the usage of const.

    However, that said, in this case your const declarations are reasonable. In particular your statement "the callee will have a const reference to m_Vertices, but will still be able to modify the Vertex instances" is not true -- because of the decisions that were made in the design of std::vector regarding const-ness.

    There are three main ways someone might want to modify the vector and its elements. The callee may want to add/delete items to/from the vector, may want to change the value of a field of the elements, or may want to replace one of the elements. A reference to a const vector will make all three of those actions errors:

    #include <iostream>
    #include <vector>
    #include <span>
    
    struct point {
        double x;
        double y;
    };
    
    class vertices {
        std::vector<point> points_;
    public:
        vertices(std::span<const point> pts) :
            points_{pts.begin(), pts.end()}
        {}
    
        const std::vector<point>& get() const {
            return points_;
        }
    
        std::vector<point>& get() {
            return points_;
        }
    };
    
    int main()
    {
        vertices verts({{ {1.0,2.0}, {3.0,4.0}, {5.0,6.0} }});
        const auto& const_verts = verts;
    
        verts.get().push_back({ 7.0,8.0 }); // <= yes.
        //const_verts.get().push_back({ 7.0,8.0 }); <= no.
    
        verts.get().front().x += 1; // <= yes.
        //const_verts.front().x += 1; // <= also no.
    
        verts.get()[2] = { 0.0,0.0 }; // <= yes.
        //const_verts.get()[2] = { 0.0,0.0 }; <= no.
    }