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

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

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

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

字符串
所以输出将是:

2 3 4 5 6


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

int main(){
    std::shared_ptr<float[]> _vals = std::make_unique<float[]>(70);
    for(uint32_t i = 0; i < 70; ++i)
        _vals[i] = i;
    //range from 2 - 5, and then range from 70-73
    std::shared_ptr<float[]> _partial(_vals, &_vals[2]);
    float** begin = &(&_vals[70]);
    float** end = &(&_vals[73]);
    float** beg_2 = &(&_partial[2]);
    for(;begin != end; ++(*begin), ++(*beg_2)){
        beg_2 = begin;
    }

    //so technically _partial[0] would point to _vals[2] but _partial[3] would point to _vals[70]
    for(uint32_t i = 0; i < 5; ++i)
        std::cout<<_partial[i]<<" ";
    std::cout<<std::endl;
    return 0;
}


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

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


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

krcsximq

krcsximq1#

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

template<typename T>
class subsequence {
public:
    struct interval { std::size_t begin, length; };
private:
    std::shared_ptr<T[]> storage;
    std::vector<interval> pieces;
public:
    subsequence(std::shared_ptr<T[]> storage, std::initializer_list<interval> pieces)
    : storage(std::move(storage)), pieces(pieces) { }

    T &operator[](std::size_t i) {
        for(interval const &piece : pieces) {
            if(i < piece.length) return storage[piece.begin + i];
            else i -= piece.length;
        }
    }
};

int main() {
    std::size_t const size = 80;
    std::shared_ptr<float[]> vals = std::make_unique<float[]>(size);
    for(uint32_t i = 0; i < size; ++i) _vals[i] = i;

    subsequence partial(vals, {{2, 3}, {70, 3}}); // range from 2 - 5, and then range from 70-73
    for(uint32_t i = 0; i < 6; ++i) std::cout << partial[i] << " ";
    std::cout << "\n";
}

字符串

vyu0f0g1

vyu0f0g12#

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

#include <iostream>
#include <memory>
#include <utility>
#include <vector>

using std::cout;
using std::exchange;
using std::ostream;
using std::pair;
using std::shared_ptr;
using std::vector;

namespace {

using shared_float_span = pair<shared_ptr<float>, shared_ptr<float>>;
using vector_span = vector<shared_float_span>;

auto operator<<(ostream& out, shared_float_span const& s) -> ostream& {
    auto sep = "";
    auto it = s.first.get();
    auto end = s.second.get();

    while (it != end) {
        out << exchange(sep, " ") << *it;
        ++it;
    }

    return out;
}

auto operator<<(ostream& out, vector_span const& v) -> ostream& {
    auto sep = "";

    for (auto&& x : v) {
        out << exchange(sep, " ") << x;
    }

    return out;
}

} // anon

int main(){
    shared_ptr<float[]> vals = std::make_unique<float[]>(99);

    for(uint32_t i = 0; i < 99; ++i) {
        vals[i] = 1000 + i;
    }

    shared_float_span a{ shared_ptr<float>(vals, &vals[2]), shared_ptr<float>(vals, &vals[6]) };
    shared_float_span b{ shared_ptr<float>(vals, &vals[70]), shared_ptr<float>(vals, &vals[74]) };
    vector_span v{a, b};
    cout << v << "\n";
}

字符串

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岁以前的答案。

相关问题