返回对 std::vector<std::unique_ptr<T>> 的引用范围

Returning a range of references to a std::vector<std::unique_ptr<T>>

我有一个抽象基 class T 和另一个 class 持有指向 T 的唯一指针向量。 class 应该支持两个函数返回对条目的引用。其中一个应该提供读取访问权限,另一个应该能够修改 unique_ptr 中的值但不能修改指针:

class A {
  private:
    std::vector<std::unique_ptr<T>> data;
  public:
    auto access() -> RefRange<T>;
    auto const_access() -> ConstRefRange<T>;
};

范围应满足std::random_access_range的要求。 如果不在 C++17 中分配额外的内存(boost 可用),我该如何做到这一点?

您可以实现一个包装器类型,它包装 std::vector::iterator 并在取消引用时提供对 std::unique_ptr 包含的元素的引用。 然后 return 一个包含这些类型的开始和结束迭代器的简单结构。

例如:

template<class T>
struct MyIterator
{
    typename std::vector<std::unique_ptr<T>>::iterator iter; // or const_iterator for the const version

    decltype(auto) operator*() const { return *iter->get(); }

   // implement the rest of the iterator functionality
   // ...
};

template<class T>
struct RefRange
{
    MyIterator<T> first, last;

    auto begin() const { return this->first; }
    auto end() const { return this->last; }
};

class A {
private:
    std::vector<std::unique_ptr<T>> data;
public:
    auto access() -> RefRange<T> { return { data.begin(), data.end() }; }
};

迭代器的包装器是我首先想到的。 这是一个简单粗暴的例子,但我想你应该明白了。

演示:https://godbolt.org/z/z365js1T9

#include <memory>
#include <vector>
#include <iostream>
#include <initializer_list>


template<typename T>
struct S
{
    using VecType = std::vector<std::unique_ptr<T>>;
    VecType v{};

    class RefRange
    {
    public:
        RefRange(VecType& v) : r{v} {}
        struct iterator
        {
            typename VecType::iterator underlying_iterator;
            //below just return const T& for ConstRefRange
            T& operator*() const { return *underlying_iterator->get();}
            iterator& operator++(){++underlying_iterator; return *this;}
            iterator operator++(int) {iterator ret{*this}; ++(*this); return ret;}

            friend bool operator==(const iterator& l, const iterator& r)
            { 
                return l.underlying_iterator==r.underlying_iterator;
            }

            friend bool operator!=(const iterator& l, const iterator& r)
            { 
                return !(l==r);
            }
        };

        iterator begin() {return iterator{r.begin()};}
        iterator end() {return iterator{r.end()};}
    
    private:
        VecType& r;
    };

    RefRange refs() {return RefRange{v};}
};

int main()
{
    S<int> s;
    s.v.push_back(std::make_unique<int>(5)); 
    s.v.push_back(std::make_unique<int>(6)); 
    s.v.push_back(std::make_unique<int>(7)); 
    
    auto r = s.refs();
    for (auto&& el : r) {std::cout << el;}
}

如果你有提升,大部分都是由范围适配器完成的indirected

class A {
  private:
    static auto add_const(T & t) -> const T & { return t; }
    std::vector<std::unique_ptr<T>> data;
    using indirected = boost::adaptors::indirected;
    using transformed = boost::adaptors::transformed;
  public:
    auto access() { return data | indirected; }
    auto const_access() const { return data | indirected | transformed(add_const); }
};

或者在 C++20 中使用 std::views::transform

class A {
  private:
    static auto indirect(const std::unique_ptr<T> & ptr) -> T & { return *ptr; }
    static auto add_const(T & t) -> const T & { return t; }
    std::vector<std::unique_ptr<T>> data;
    using transform = std::views::transform;
  public:
    auto access() { return data | transform(indirect); }
    auto const_access() const { return data | transform(indirect) | transform(add_const); }
};

如果你有 <experimental/propagate_const>,我会用它来代替 transform(add_const)

class A {
  private:
    std::vector<std::experimental::propagate_const<std::unique_ptr<T>>> data;
    using indirected = boost::adaptors::indirected;
  public:
    auto access() { return data | indirected; }
    auto const_access() const { return data | indirected; }
};