c++ 有没有办法在不同的范围内共享shared_ptr的一部分?

ylamdve6  于 2023-08-09  发布在  其他
关注(0)|答案(4)|浏览(110)

因此,如果你想共享shared_ptr过去元素x中的所有内容,你可以写如下代码:

  1. int main(){
  2. std::shared_ptr<float[]> _vals = std::make_unique<float[]>(70);
  3. for(uint32_t i = 0; i < 10; ++i)
  4. _vals[i] = i;
  5. //range from 2 - end of _vals
  6. // x = 2 in this case
  7. std::shared_ptr<float[]> _partial(_vals, &_vals[2]);
  8. for(uint32_t i = 0; i < 5; ++i)
  9. std::cout<<_partial[i]<<" ";
  10. std::cout<<std::endl;
  11. return 0;
  12. }

字符串
所以输出将是:

  1. 2 3 4 5 6


所以_partial将从_vals.get() + 2指向_vals的末尾。但是,如果我想让它指向例如(2-5), (70-end),我想知道我是否可以为_partial分配地址来完成这一点呢?例如,如果这起作用:

  1. int main(){
  2. std::shared_ptr<float[]> _vals = std::make_unique<float[]>(70);
  3. for(uint32_t i = 0; i < 70; ++i)
  4. _vals[i] = i;
  5. //range from 2 - 5, and then range from 70-73
  6. std::shared_ptr<float[]> _partial(_vals, &_vals[2]);
  7. float** begin = &(&_vals[70]);
  8. float** end = &(&_vals[73]);
  9. float** beg_2 = &(&_partial[2]);
  10. for(;begin != end; ++(*begin), ++(*beg_2)){
  11. beg_2 = begin;
  12. }
  13. //so technically _partial[0] would point to _vals[2] but _partial[3] would point to _vals[70]
  14. for(uint32_t i = 0; i < 5; ++i)
  15. std::cout<<_partial[i]<<" ";
  16. std::cout<<std::endl;
  17. return 0;
  18. }


但是,如果我尝试编译它,我会得到错误:

  1. cannot take the address of an rvalue of type 'float *'
  2. float** begin = &(&_vals[70]);


有没有一种方法可以完成我正在努力做的事情?

krcsximq

krcsximq1#

shared_ptr不适合这项工作。一个shared_ptr拥有某个对象并指向某个(可能不同的)对象。它 * 没有 * 实现足够的逻辑来执行0 -> 0, 1 -> 1, 2 -> 2, 3 -> 70之类的Map。(它也不应执行这一逻辑。这不是一项简单的任务。)
实现您自己的类以实现此行为。这应该可以帮助您开始:

  1. template<typename T>
  2. class subsequence {
  3. public:
  4. struct interval { std::size_t begin, length; };
  5. private:
  6. std::shared_ptr<T[]> storage;
  7. std::vector<interval> pieces;
  8. public:
  9. subsequence(std::shared_ptr<T[]> storage, std::initializer_list<interval> pieces)
  10. : storage(std::move(storage)), pieces(pieces) { }
  11. T &operator[](std::size_t i) {
  12. for(interval const &piece : pieces) {
  13. if(i < piece.length) return storage[piece.begin + i];
  14. else i -= piece.length;
  15. }
  16. }
  17. };
  18. int main() {
  19. std::size_t const size = 80;
  20. std::shared_ptr<float[]> vals = std::make_unique<float[]>(size);
  21. for(uint32_t i = 0; i < size; ++i) _vals[i] = i;
  22. subsequence partial(vals, {{2, 3}, {70, 3}}); // range from 2 - 5, and then range from 70-73
  23. for(uint32_t i = 0; i < 6; ++i) std::cout << partial[i] << " ";
  24. std::cout << "\n";
  25. }

字符串

展开查看全部
vyu0f0g1

vyu0f0g12#

如果分散跨度需要原始数组的共享指针,则可以在共享指针对中捕获该信息,然后将其放入这些对的向量中。
我冒昧地将原始数组设为99个元素,因此第二个跨度将是有效的。

  1. #include <iostream>
  2. #include <memory>
  3. #include <utility>
  4. #include <vector>
  5. using std::cout;
  6. using std::exchange;
  7. using std::ostream;
  8. using std::pair;
  9. using std::shared_ptr;
  10. using std::vector;
  11. namespace {
  12. using shared_float_span = pair<shared_ptr<float>, shared_ptr<float>>;
  13. using vector_span = vector<shared_float_span>;
  14. auto operator<<(ostream& out, shared_float_span const& s) -> ostream& {
  15. auto sep = "";
  16. auto it = s.first.get();
  17. auto end = s.second.get();
  18. while (it != end) {
  19. out << exchange(sep, " ") << *it;
  20. ++it;
  21. }
  22. return out;
  23. }
  24. auto operator<<(ostream& out, vector_span const& v) -> ostream& {
  25. auto sep = "";
  26. for (auto&& x : v) {
  27. out << exchange(sep, " ") << x;
  28. }
  29. return out;
  30. }
  31. } // anon
  32. int main(){
  33. shared_ptr<float[]> vals = std::make_unique<float[]>(99);
  34. for(uint32_t i = 0; i < 99; ++i) {
  35. vals[i] = 1000 + i;
  36. }
  37. shared_float_span a{ shared_ptr<float>(vals, &vals[2]), shared_ptr<float>(vals, &vals[6]) };
  38. shared_float_span b{ shared_ptr<float>(vals, &vals[70]), shared_ptr<float>(vals, &vals[74]) };
  39. vector_span v{a, b};
  40. cout << v << "\n";
  41. }

字符串

展开查看全部
c86crjj0

c86crjj03#

智能指针都是关于所有权的--谁拥有你分配的那块内存?你不能让一个智能指针拥有整个数组,而另一个只拥有其中的一部分,这在物理上是不可能的。

t5zmwmid

t5zmwmid4#

所以_partial将从_vals.get() + 2指向_vals的末尾。
不可以。指针指向单个对象,而不是一个范围。在本例中,_partial指向_vals.get() + 2。只有一个元素。这样就可以访问_vals的某个子范围的想法是基于您对如何访问元素所采用的约定。
举个例子:你的循环打印了_vals的第三到第七个元素。您的文本建议循环应该“到_vals的末尾”(第70个元素),但它没有。为什么?因为你的循环结束于索引5而不是68。这不是_partial固有的,而是您如何使用_partial的结果。
类似地,循环从索引0(_partial的索引)开始。这是你的选择虽然从索引0开始是常规的,但它仅仅是常规的。在您的特定情况下,_partial指向数组的第三个元素。这意味着表达式_partial.get()[-2]是法律的的,并且指向数组的第一个元素。(表达式_partial[-2]在实践中可能也适用,但cppreference.com说该行为是未定义的。)您的起点,就像您的终点一样,不是_partial固有的东西,而是您如何使用_partial的结果。
结果是,你想要的行为不是关于所有权,而是关于如何使用你所拥有的数据。

重新构建问题

因此,我们可以从您的问题中删除共享所有权。你有一个数组,你需要一种方法来迭代数组的子范围。阵列的所有方式无关紧要;该问题仅需要存在数组和指定子范围的方式。
简单的情况是当子范围是连续的时。在这种情况下,子范围可以由其开始和结束来指定。一种常见的方法是提供一个指向开始的指针(_partial)和一个大小(5,如果我们按照你的循环,或者68,如果我们按照“_vals的结尾”)。你知道怎么做。你的问题是如何指定一个不连续的子范围。
非连续范围所面临的问题是std::shared_ptr::operator[]和内置的[]运算符是为连续范围设计的。对象_partial[2]_partial[3]必须位于相邻的内存位置。这就是这个操作员的工作方式。

答案

那么我们能做些什么呢?在C17中,你可以写一个适配器类。这样一个类可以 Package 一个数组并提供一个operator[]来执行您想要的操作。也就是说,它将具有用于阵列访问的数据成员(例如,你的std::shared_ptr<float[]>),它会以某种方式存储你想要访问的索引。我将把细节留给读者(或其他答案),因为这个答案花了很长时间才达到这一点。
虽然这个问题被标记为C
17,但我会注意到在C++20中,有范围。特别是,filter view可以用来选择您想要的底层视图的元素。例如,您可以为过滤器视图提供一个 predicate ,当参数minus _vals的地址介于2和5之间或介于60和65之间时,该 predicate 通过(返回true)。我将把细节留给读者,因为这个问题要求20岁以前的答案。

展开查看全部

相关问题