Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

const method in a class returning vector of pointers

Say I have this class:

#include <vector>
using namespace std;

class Bag
{
    vector<int*> items;

public:
    void addItem(int* i) 
    { 
        items.push_back(i); 
    }
    const vector<int*> getItems() const
    {
        return items;
    }
};

The problem is I want to prevent changes of the values pointed by the pointers in parameter items. But using the vector returning from getItems I'm allowed to change the pointed values.

Now, in order to fix that I can declare the parameter items as vector<const int*> but then I can't change the values in my class either. Is there any other way to protect the values but still not using const in the items declarations?

like image 605
Adam Avatar asked Oct 14 '15 13:10

Adam


2 Answers

As @KerrekSB pointed out, you need to reconstruct the structure, changing the type of the element to a const one:

std::vector<const int*> getItems() const { 
    return std::vector<const int*>(items.begin(), items.end());
}

Here the constructor of vector<const int*> utilizes the implicit conversion from int* to const int*. You can't just return items here, because vector<T> is invariant on T. This directly means that there's no way to convert from vector<int*> to vector<const int*> just by a type cast.


To address your comment:

Is this the only way? I feel like creating a copy is inefficient.

As for the copy, it copies only the pointer data, so typically 8 bytes per element. For a vector of pointers to larger structures, it's typically negligible, but for a vector of ints it indeed is a rather large overhead.

Which brings us to the question: why are you storing pointers in the first place? Simply store vector<int>, and then you can just return a const& to it.

like image 134
Bartek Banachewicz Avatar answered Oct 24 '22 01:10

Bartek Banachewicz


If you want to avoid copies and allow read-only access to your std::vector elements, you could instead provide functions to get a const_iterator into the container:

class Bag
{
    vector<int*> items;

public:
    void addItem(int* i) 
    { 
        items.push_back(i); 
    }

    //could just be called begin if that gives the correct idea
    vector<int*>::const_iterator itemsBegin() const
    {
        return std::cbegin(items);
    }

    vector<int*>::const_iterator itemsEnd() const
    {
        return std::cend(items);   
    }
};

Then you would loop over the Bag contents using that iterator rather than getting out a whole vector of items. If you provided begin and end functions then you could even use range-based for-loops.

like image 20
TartanLlama Avatar answered Oct 24 '22 01:10

TartanLlama