Search code examples
c++const-correctness

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.


Solution

  • You're asking for std::experimental::propagate_const. But since it is an experimental feature, there is no guarantee that any specific toolchain is shipped with an implementation. You may consider implementing your own. There is an MIT licensed implementation, however. After including the header:

    using namespace xpr=std::experimental;
    ///...
    std::vector<xpr::propagate_const<int*>> my_ptr_vec;
    

    Note however that raw pointer is considered evil so you may need to use std::unique_ptr or std::shared_ptr. propagate_const is supposed to accept smart pointers as well as raw pointer types.