Turning vector of shared_ptr into vector of shared_ptr to const

家住魔仙堡 提交于 2020-01-24 03:05:35

问题


Let

class A
{
    std::vector<std::shared_ptr<int>> v_;
};

Now I'd like to add access to v_ using two public member functions

std::vector<std::shared_ptr<int>> const & v() { return v_; }

and

std::vector<std::shared_ptr<int const> const & v() const { TODO }

I cannot replace TODO with return v_; though.

One option would be to not return a reference but a copy. Apart from the obvious performance penalty, this would also make the interface somewhat less desirable.

Another option is to make TODO equal to return reinterpret_cast<std::vector<std::shared_ptr<int const>> const &>(v_);

My question is, is this undefined behavior? Or, alternatively, is there a better option, preferably without using reinterpret_cast?


回答1:


A way to avoid copying the container is to provide transform iterators that transform the element on dereference:

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



回答2:


Putting aside the discussion of whether or not you should return a reference to a member...

std::vector already propagates its own const qualifier to the references, pointee's and iterators it returns. The only hurdle is making it propagate further to the pointee type of the std::shared_ptr. You can use a class like std::experimental::propagate_const (that will hopefully be standardized) to facilitate that. It will do as its name implies, for any pointer or pointer-like object it wraps.

class A
{
    using ptr_type = std::experimental::propagate_const<std::shared_ptr<int>>;
    std::vector<ptr_type> v_;
};

Thus TODO can become return v_;, and any access to the pointees (like in the range-based for you wish to support) will preserve const-ness.

Only caveat is that it's a moveable only type, so copying out an element of the vector will require a bit more work (for instance, by calling std::experimental::get_underlying) with the element type of the vector itself.



来源:https://stackoverflow.com/questions/48641861/turning-vector-of-shared-ptr-into-vector-of-shared-ptr-to-const

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!