将shared_ptr的向量转换为shared_ptr的向量为const

时间:2018-02-06 11:22:21

标签: c++ vector undefined-behavior reinterpret-cast const-correctness

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 个答案:

答案 0 :(得分:6)

避免复制容器的一种方法是提供转换迭代器,在迭代引用时转换元素:

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

答案 1 :(得分:3)

暂不讨论是否应该返回对成员的引用......

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)与向量本身的元素类型。