Превращение вектора shared_ptr в вектор shared_ptr в const
Позволять
class A
{
std::vector<std::shared_ptr<int>> v_;
};
Теперь я хотел бы добавить доступ к v_
используя две открытые функции-члены
std::vector<std::shared_ptr<int>> const & v() { return v_; }
а также
std::vector<std::shared_ptr<int const> const & v() const { TODO }
Я не могу заменить TODO
с return v_;
хоть.
Один из вариантов - не возвращать ссылку, а копию. Помимо очевидного снижения производительности, это также сделало бы интерфейс несколько менее желательным.
Другой вариант - сделать TODO
равно return reinterpret_cast<std::vector<std::shared_ptr<int const>> const &>(v_);
Мой вопрос, это неопределенное поведение? Или, альтернативно, есть лучший вариант, предпочтительно без использования reinterpret_cast
?
2 ответа
Чтобы избежать копирования контейнера, нужно предоставить итераторы преобразования, которые преобразуют элемент при разыменовании:
#include <vector>
#include <memory>
#include <boost/iterator/transform_iterator.hpp>
class A
{
std::vector<std::shared_ptr<int> > v_;
struct Transform
{
template<class T>
std::shared_ptr<T const> operator()(std::shared_ptr<T> const& p) const {
return p;
}
};
public:
A() : v_{std::make_shared<int>(1), std::make_shared<int>(2)} {}
using Iterator = boost::transform_iterator<Transform, std::vector<std::shared_ptr<int> >::const_iterator>;
Iterator begin() const { return Iterator{v_.begin()}; }
Iterator end() const { return Iterator{v_.end()}; }
};
int main() {
A a;
// Range access.
for(auto const& x : a)
std::cout << *x << '\n';
// Indexed access.
auto iterator_to_second_element = a.begin() + 1;
std::cout << **iterator_to_second_element << '\n';
}
Оставляя в стороне обсуждение того, стоит ли возвращать ссылку члену...
std::vector
уже размножается самостоятельно const
определитель ссылок, указателей и итераторов, которые он возвращает. Единственное препятствие - заставить его распространяться дальше к типу std::shared_ptr
, Вы можете использовать класс как std::experimental::propagate_const
(это, будем надеяться, будет стандартизировано), чтобы облегчить это. Он будет делать то, что подразумевает его имя, для любого указателя или объекта, подобного указателю, который он переносит.
class A
{
using ptr_type = std::experimental::propagate_const<std::shared_ptr<int>>;
std::vector<ptr_type> v_;
};
таким образом TODO
может стать return v_;
и любой доступ к пуанте (как в диапазоне, для которого вы хотите поддерживать) сохранит постоянство.
Единственное предостережение в том, что это только подвижный тип, поэтому копирование элемента вектора потребует немного больше работы (например, путем вызова std::experimental::get_underlying
) с типом элемента самого вектора.