Превращение вектора 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) с типом элемента самого вектора.

Другие вопросы по тегам