Search code examples
c++c++17shared-ptrunique-ptr

Is there a way to share part of a shared_ptr in different ranges?


So, if you wanted to share everything in a shared_ptr past element x you could write something like the following:

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

so the output would be:

2 3 4 5 6

So _partial would point from _vals.get() + 2 to the end of _vals. However what if I wanted to have it point to for example (2-5), (70-end) for example, I was wondering if I could assign addresses to _partial to accomplish this? So if for example this worked:

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

However, if I try to compile this, I get the error:

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

Is there a way to accomplish what I am trying to do?


Solution

  • A shared_ptr is the wrong tool for this job. A shared_ptr owns some object and points to some (potentially different) object. It does not implement enough logic to perform a mapping like 0 -> 0, 1 -> 1, 2 -> 2, 3 -> 70 like you want. (Nor should it implement this logic. It's not a simple task.)

    Implement your own class to implement this behavior. This should get you started:

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