Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two std::vectors pointing at nearly the same data, is that possible

I have an algorithm which expects a std::vector (call it A). However, I already have B with N + 2entries and what I basically want is to pass B.data() + 2, so the algorithm gets the last N entries from B. If A is modified, then so is B.

When using double* pointers it's perfectly clear how I should make it, but is this also possible with std::vectors? I mean, the nice thing with a vector is that it handles the memory for me, and what I want now is to forbid it (if B or A get destroyed, they should leave the pointed data untouched).

Like this:

std::vector< double > B({1,2,3,4,5});
std::vector< double > A(B.data() + 2, B.size() - 2);

// A and B share data now. A is 3, 4, 5

I know that the algorithm could be designed better for this purpose by taking a pair of iterators, but that's not in my hands.

UPDATE

(In the comments, one wished to see the signature, here it is)

nlopt::result nlopt::opt::optimize(std::vector<double> &x, double &opt_f);

However, my original intention was to be very clever and let the algorithm optimize direclty in my vector B, so what I ended up with was something like this:

std::vector< double > B(N + 2);
// do something with it here, like put initial values

std::vector< double > A( B.begin() + 2, B.end() );
optimize( A );

std::copy(A.begin(), A.end(), B.begin() + 2);

I really don't worry about this workaround, I also read in the documentation of nlopt that the vectors are copies internally anyway a number of times if one uses the C++ interface instead of the C one.

But again, this example really opened my eyes regarding algorithm interface design and that it's worth to invest some time browsing through e.g. the boost libraries like range and so on.

like image 604
wal-o-mat Avatar asked Oct 23 '13 08:10

wal-o-mat


People also ask

How can you tell if two vectors are identical?

The C++ function std::vector::operator== tests whether two vectors are equal or not. Operator == first checks the size of both container, if sizes are same then it compares elements sequentially and comparison stops at first mismatch.

How do you check if a vector is equal to another vector?

In simple words, we can say that two or more vectors are said to be equal vectors if their length is the same and they all point in the same direction. Generally, we can check for equal vectors by comparing their coordinates. If all the coordinates of two or more vectors are the same, then they are equal vectors.

Is std::vector on the stack?

Although std::vector can be used as a dynamic array, it can also be used as a stack. To do this, we can use 3 functions that match our key stack operations: push_back() pushes an element on the stack. back() returns the value of the top element on the stack.

How does std::vector allocate memory?

Vectors are assigned memory in blocks of contiguous locations. When the memory allocated for the vector falls short of storing new elements, a new memory block is allocated to vector and all elements are copied from the old location to the new location. This reallocation of elements helps vectors to grow when required.


1 Answers

Actually, I had a similar problem in a project, this is the solution I came up with:

#include <iterator>
#include <type_traits>

/** \brief Provides an interface to random accessible ranges
  * \tparam RAIterator must be a random access iterator
  *
  * This class doesn't allocate any memory by itself and
  * provides only an interface into the data of another
  * container.
  * \attention Keep in mind that although all methods are
  * \c const, the returned RAIterator might not be a \c const_iterator
  * at all. It is your responsibility to make sure that
  * you don't invalidate the given range while working on it.
**/

template <class RAIterator>
class Slice
{
public:
    //! Typedef for convenience when working with standard algorithms
    typedef RAIterator iterator;

    //! Alias to the iterator's reference type
    typedef typename std::iterator_traits<RAIterator>::reference reference;

    //! Creates the slice.
    //! \param first, last a valid range
    Slice(RAIterator first, RAIterator last) : first(first), last(last){}

    //! Creates the slice.
    //! \param first iterator to the first element
    //! \param length of the range [first, first+length)
    //! \remark if length is negative, an empty slice is assumed.
    Slice(RAIterator first, int length) : 
        first(first), 
        last( length > 0 ? first + length : first)
    {}

    //! The default constructor is deleted, as it would resemble an empty slice
    Slice() = delete;

    ///@{
    //! \brief Defaulted construcors.
    Slice(const Slice&) = default;
    Slice(Slice&&) = default;
    Slice& operator=(const Slice&)= default;
    Slice& operator=(Slice&&)= default;
    /**@}*/

    //! Returns an iterator to the begin of the range
    RAIterator begin() const{ return first; }

    //! Returns an iterator to the end of the range
    RAIterator end()   const{ return last;  }

    //! Returns the size of the slice.
    typename std::iterator_traits<RAIterator>::difference_type size() const{
        return std::distance(first, last);
    }

    //! Provides random access for the values interfaced by Slice
    reference operator[](size_t index) const { return first[index]; }

private:
    RAIterator first; //!< begin of the range
    RAIterator last;  //!< end of the range
};

/** \brief Creates a slice from the given range
  * \tparam RAIterator should be an random access iterator
  * \returns a slice [first,last)
  * \param first, last is the range
**/
template <class RAIterator>
Slice<RAIterator> make_slice(RAIterator first, RAIterator last){
    return Slice<RAIterator>(first, last);
}

Now you can use the Slice just as in your example:

std::vector< double > B({1,2,3,4,5});
Slice A(B.begin() + 2, B.size() - 2);
A[0] = 5;
// B == {1,2,5,4,5}

EDIT: If you want a more mature slice, use boost::adaptors::slice if possible.

like image 59
Zeta Avatar answered Sep 28 '22 09:09

Zeta