Два std::vectors, указывающие на почти те же данные, возможно

У меня есть алгоритм, который ожидает std::vector (назовите его A). Однако у меня уже есть B с элементами N + 2, и я в основном хочу передать B.data() + 2, поэтому алгоритм получает последние N записи из B. Если A изменено, то и B.

При использовании указателей double* это совершенно ясно, как я должен это сделать, но возможно ли это также с std::vector s? Я имею в виду, что хорошая вещь с вектором заключается в том, что он обрабатывает память для меня, и теперь я хочу ее запретить (если B или A будут уничтожены, они должны оставить заостренные данные нетронутыми).

Вот так:

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

Я знаю, что для этой цели алгоритм может быть лучше разработан, взяв пару итераторов, но это не в моих руках.

ОБНОВЛЕНИЕ

(В комментариях хотелось бы видеть подпись, вот она)

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

Однако мое первоначальное намерение состояло в том, чтобы быть очень умным и позволить алгоритму оптимизировать direclty в моем векторе B, поэтому у меня получилось что-то вроде этого:

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);

Я действительно не беспокоюсь об этом обходном пути, я также прочитал в документации nlopt, что векторы являются внутренними копиями в любом случае несколько раз, если использовать С++-интерфейс вместо C.

Но опять же, этот пример действительно открыл мне глаза на дизайн интерфейса алгоритма и что стоит потратить некоторое время на просмотр через, например, библиотеки ускорения, такие как range и т.д.

Ответ 1

Собственно, у меня была аналогичная проблема в проекте, это решение, с которым я столкнулся:

#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 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);
}

Теперь вы можете использовать Slice так же, как в вашем примере:

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. Если вы хотите более зрелый фрагмент, используйте boost::adaptors::slice, если это возможно.

Ответ 2

Только две идеи.

  • Если функция ссылается на std::vector - попробуйте создать свой класс, используя std::vector в качестве базового класса.

  • Попробуйте использовать распределитель, который является вторым параметром шаблона для std::vector. С его помощью вы можете выделить или просто использовать уже выделенную память по своему усмотрению. http://www.cplusplus.com/reference/vector/vector/ http://www.cplusplus.com/reference/vector/vector/get_allocator/

Ответ 3

Просто сумасшедший ответ.

Я думал об использовании vector of shared_ptr<double>, где каждый shared_ptr указывает на отдельный double. Таким образом, он выглядит уродливым и может быть болезненным, те vectors будут совместно использовать память, содержащую doubles.