繁体   English   中英

返回对 std::vector 的引用范围<std::unique_ptr<t> &gt; </std::unique_ptr<t>

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

我有一个抽象基础 class T和另一个 class 持有指向T的唯一指针向量。 class 应该支持两个 function 返回对条目的引用。 其中一个应该提供读取访问权限,另一个应该能够修改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 中分配额外的 memory(升压可用),我该如何做到这一点?

如果你有提升,这大部分是由范围适配器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; }
};

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

前任:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM