std::vector< MyObj* > to std::vector< MyObj Const * >, how to no loops?

Viewed 102

Title,

Let say we have

std::vector<MyObj*> ArrayOfPtr;

std::vector<MyObj const *> ArrayOfPtrToConstObj;

int main()
{
     //I'd like to give ArrayOfPtr to another obj/function but not give it the right to modify the objects in it.

    ArrayOfPtrToConstObj = ArrayOfPtr;

    Function(ArrayOfPtrToConstObj)
}

Why can't that be written in C++ and understood automatically by the compiler considering const does not alter the logic (optimization or anything)..?

Is there a quick way to go from vector 1 to vector 2 without having to for/while loop onto the array and fill the second one?

2 Answers

Well, someone, somewhere is going to have to loop. You can avoid writing the loop yourself by using vector::assign:

ArrayOfPtrToConstObj.assign(ArrayOfPtr.begin(), ArrayOfPtr.end());

But assign is still going to loop.

The preferred modern solution is to either pass an const-iterator/pointer pair and let the user consume them, or to use a view class like gsl::span. This requires no copying of anything, and can add const as needed:

gsl::span<MyObj const*> spn(ArrayOfPtr.data(), ArrayOfPtr.size());

vector2 is a fundamentally different type. If, however, you just want to view the inside of vector1 in a const manner, I have a solution for you.

I'd start with something like, but not exactly, gsl::span.

template<class T>
struct span {
private:
  T* b = 0;
  T* e = 0;
public:
  T* begin() const { return b; }
  T* end() const { return e; }
  T* data() const { return begin(); }

  template<class U>
  using compatible = std::enable_if_t< std::is_convertible< U*, T* >{} && sizeof(T)==sizeof(U), bool >;

  template<class U,
    compatible<U> =true
  >
  span( U* s, U* f ): b(s), e(f) {}
  template<class U,
    std::enable_if_t< std::is_convertible< U*, T* >{} && sizeof(T)==sizeof(U), bool > =true
  >
  span( U* s, std::size_t length ): span( s, s+length ) {}

  // pointer semantics:
  span()=default;
  span(span const&)=default;
  span& operator=(span const&)=default;
  ~span()=default;         

  std::size_t size() const{ return end()-begin(); }
  bool empty() const{ return end()==begin(); }
  T& front() const { return *begin(); }
  T& back() const { return *(end()-1); }

  // sub spans:
  span without_front( std::size_t n=1 ) const {
    n = (std::min)(n, size());
    return {begin()+n, end()};
  }
  span without_back( std::size_t n=1 ) const {
    n = (std::min)(n, size());
    return {begin(), end()-n};
  }
  span only_front( std::size_t n=1 ) const {
    n = (std::min)(n, size());
    return {begin(), begin()+n};
  }
  span only_back( std::size_t n=1 ) const {
    n = (std::min)(n, size());
    return {end()-n, end()};
  }
  span sub( std::size_t start, std::size_t length ) const {
    return without_front(start).only_front(length);
  }
  T& operator[](std::size_t I)const{ return begin()[I]; }
  T& at(std::size_t I)const{
     if (I>=size()) throw std::out_of_range{"index"};
     return begin()[I];
  }
  std::vector<std::decay_t<T>> as_vector()const& {
    return {begin(), end()};
  }
  std::vector<std::decay_t<T>> as_vector()&& {
    return {std::make_move_iterator(begin()), std::make_move_iterator(end())};
  }
  template<class C,
    compatible< std::decay_t< decltype( *std::declval<C&>().data() ) > > =true,
    std::enable_if_t< !std::is_same<span, std::decay_t<C>>{}, bool > =true
  >
  span( C&& c ): span(c.data(), c.size()) {}
};

If you do not intend to change the length of a vector in a context, a span is the right answer.

Live example with some tests.

Function in this case should take span< MyObj const*const >, stating it just wants to look at a buffer of MyObj consts. You can store a span of this type if you want to, or let implicit conversion work:

std::vector<MyObj*> ArrayOfPtr;

span<MyObj const*const> SpanOfPtrToConstObj;

int main()
{
  //I'd like to give ArrayOfPtr to another obj/function but not give it the right to modify the objects in it.

  SpanOfPtrToConstObj = ArrayOfPtr;

  Function(SpanOfPtrToConstObj)
}

Function cannot modify the length of the vector or the pointers within it or what the pointers point to.

Related