Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I propagate const when returning a std::vector<int*> from a const method?

Lets show it in an example where we have a Data class with primary data, some kind of index that points to the primary data, and we also need to expose a const version the index.

class Data
{
public:
  const std::vector<int>& getPrimaryData() const { return this->primaryData; }
  const std::vector<int*>& getIndex() const { return this->index; }
private:
  std::vector<int> primaryData;
  std::vector<int*> index;
};

This is wrong, as the user can easily modify the data:

const Data& data = something.getData();
const std::vector<int*>& index = data.getIndex();
*index[0] = 5; // oups we are modifying data of const object, this is wrong

The reason of this is, that the proper type the Data::getIndex should be returning is:

const std::vector<const int*>&

But you can guess what happens when you try to write the method that way to "just convert the non-const variant to const variant":

// compiler error, can't convert std::vector<int*> to std::vector<const int*> these are unrelated types.
const std::vector<const int*>& getIndex() const { return this->index; }

As far as I know, C++ doesn't have any good solution to this problem. Obviously, I could just create new vector, copy the values from the index and return it, but that doesn't make any sense from the performance perspective.

Please, note that this is just simplified example of real problems in bigger programs. int could be a bigger object (Book lets say), and index could be index of books of some sort. And the Data might need to use the index to modify the book, but at the same time, provide the index to read the books in a const way.

like image 755
kovarex Avatar asked Aug 31 '25 15:08

kovarex


2 Answers

In C++20, you can just return a std::span with elements of type const int*

#include <vector>
#include <span>

class Data
{
public:
  std::span<const int* const> getIndex() const { return this->index; }
private:
  std::vector<int*> index;
};

int main() {
  const Data data;
  const auto index = data.getIndex();
  *index[0] = 5;  // error: assignment of read-only location
}

Demo

like image 161
康桓瑋 Avatar answered Sep 05 '25 16:09

康桓瑋


Each language has its rules and usages... std::vector<T> and std::vector<const T> are different types in C++, with no possibility to const_cast one into the other, full stop. That does not mean that constness is broken, it just means it is not the way it works.

For the usage part, returning a full container is generally seen as a poor encapsulation practice, because it makes the implementation visible and ties it to the interface. It would be better to have a method taking an index and returning a pointer to const (or a reference to a pointer to const if you need it):

const int* getIndex(int i) const { return this->index[i]; }

This works, because a T* can be const_casted to a const T *.

like image 20
Serge Ballesta Avatar answered Sep 05 '25 18:09

Serge Ballesta