Process vector <int *> as vector <const int *> without copying (C ++ 0x)
The class contains a std::vector<int*>
. External code needs read-only access to this vector; it must not modify the contents (neither pointers nor their contents). Inside the class, values ββcan change (for example, double_values()
, and therefore saving them as std::vector<const int*>
impossible).
Is there a way to return std::vector<int*>
as std::vector<const int*>
without making a copy? It seems to be because const just works at compile time to say what can and cannot change.
Code: (compile with g++ -std=c++0x
)
class ReadOnlyAccess { public: ReadOnlyAccess(const std::vector<int*> & int_ptrs_param): int_ptrs(int_ptrs_param) { } const std::vector<int*> & get_int_ptrs() const { return int_ptrs; } std::vector<const int*> safely_get_int_ptrs() const { // will not compile (too bad): // return int_ptrs; // need to copy entire vector std::vector<const int*> result(int_ptrs.size()); for (int k=0; k<int_ptrs.size(); k++) result[k] = int_ptrs[k]; return result; } void double_values() { for (int*p : int_ptrs) *p *= 2; } void print() const { for (const int * p : int_ptrs) std::cout << *p << " "; std::cout << std::endl; } private: std::vector<int*> int_ptrs; }; int main() { ReadOnlyAccess roa(std::vector<int*>{new int(10), new int(20), new int(100)}); std::vector<const int*> safe_int_ptrs = roa.safely_get_int_ptrs(); // does not compile (good) // *safe_int_ptrs[0] = -100000; roa.print(); const std::vector<int*> & int_ptrs = roa.get_int_ptrs(); // changes are made to the internal class values via the accessor! nooooo! *int_ptrs[0] = -100000; roa.print(); return 0; }
Returning a vector will mean copy if you want to keep const pointers anyway.
However, if your goal is to provide a way to use values ββwithout modifying them or changing their container, then an algorithm based on visitor templates can be a very good solution, in particular now when we can use lambda expressions:
#include <vector> #include <iostream> class Data { public: //...whatever needed to fill the values // here we assume that Func is equivalent to std::function< void ( int )> or std::function< void (const int& ) > and can return anything that will be ignored here. template< class Func > void for_each_value( Func func ) const // read-only { for( const int* value : m_values ) // implicit conversion { func( *value ); // read-only reference (const &), or copy // if func needs to work with the adress of the object, it still can by getting a reference to it and using & to get it adress } } void print() const { std::cout << "\nData values: \n"; for_each_value( []( const int value ) { std::cout << " "<< value << '\n'; } ); } void count_values() const { return m_values.size(); } private: std::vector<int*> m_values; }; int main() { Data data; // ... whatever needed to fill the data data.print(); std::vector<int> modified_values; data.for_each_value( [&]( int value ) { modified_values.push_back( value + 42 ); } ); return 0; }
If you understand this, and various ways of using these values ββcan be reduced to several algorithms with full generics, then this will simplify your code and allow you to store data inside your structures, rather than exposing them to the guts.
You can provide a representation of const values ββthrough custom iterators. An easy way would be to use boost::iterator
:
#include <boost/iterator/indirect_iterator.hpp> class ReadOnlyAccess { // ... typedef boost::indirect_iterator<const int* const*, const int> const_val_iter_type; const_val_iter_type cval_begin() { return it_t{const_cast<const int* const*>(&int_ptrs[0])}; } } int main() { // ... auto x = roa.cval_begin(); std::cout << x[0] <<' ' << x[1] << x[2] <<'\n'; // we can still access the pointers themselves via .base() member function: for (int i=0; i<3; ++i) assert(x.base()[i] == safe_int_ptrs[i]); // the values are read-only, the following does not compile: // x[0] = -1; // **x.base() = -1; // *x.base() = nullptr; }
If we used boost::indirect_iterator<typename std::vector<int*>::const_iterator, const int>
for const_val_iter_type
, we could change the specified values ββvia .base()
(but not like, for example, x[0] = -1
), so this solution is not general.