返回对 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; }
};
我有一个抽象基 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; }
};