HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Simple sparse array

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
arraysimplesparse

Problem

Today, I tried to create a really simple sparse_array container adapter. I did not provide all the STL-like functions, only the elementary ones as a proof of concept. I also trimmed the class from its copy constructor and all the unecessary things for this review actually. Here is the code:

#include 
#include 

template class Container=std::map>
struct sparse_array
{
    class proxy
    {
        sparse_array& sp;
        std::size_t index;

        proxy(sparse_array& sp, std::size_t index):
            sp(sp),
            index(index)
        {}

        public:
        proxy(const proxy& other) = default;
        auto operator=(const proxy& other)
            -> proxy&
        {
            sp.data[index] = T(other);
            return *this;
        }

        auto operator=(const T& val)
            -> proxy&
        {
            if (val != sp.default_value)
            {
                sp.data[index] = val;
            }
            else
            {
                sp.data.erase(index);
            }
            return *this;
        }  

        operator T() const
        {
            auto res = sp.data.find(index);
            if (res != sp.data.end())
            {
                return res->second;
            }
            return sp.default_value;
        }

        friend class sparse_array;
    };

    auto operator[](std::size_t index)
        -> proxy
    {
        return { *this, index };
    }

    auto operator[](std::size_t index) const
        -> const proxy
    {
        return { *this, index };
    }

    auto size() const
        -> std::size_t
    {
        return data.size();
    }

    private:
    T default_value = {};
    Container data;
};


What I tried to do is to have a sparse array whose number of elements is strictly equal to the number of elements that are different from default_value, hence the elements that are deleted when default_value is assigned to them. Here comes a small example:

```
#include

int main

Solution

The primary question I have is one of usage. By the name sparse_array, it sounds like you want to expose an interface that is similar to an array. Perhaps it needs dynamic sizing, or perhaps not, but in particular I expect to be able to iterate over its elements with code like this:

sparse_array sp;
// ::: fill array here :::

for (int elt: sp)
    std::cout  out_it(std::cout, ", ");
std::copy(std::begin(sp), std::end(sp), out_it);


It needs to be clear what the output from that would be. Would it include the default values in most indices? Or is this usage prohibited (perhaps due to that unclarity), as currently there are no iterator related methods? Maybe this is just part of what you intentionally excluded from the review, but it leaves so much unanswered, including things that probably come back to your proxy class, such as how *std::begin(sp) = 3 will work.

The one definite gap I see has to do with operator&. Will you support code that looks like normal array-style addressing? Right now the following code will certainly not work correctly; should it fail to compile, or should it modify sp[5]?

auto spot = &sp[4];
*(spot + 1) = 5;


Other comments:

  • I think your proxy approach here is justified for the usage you want to offer.



  • It's unclear what values are permissible as alternatives to std::map for your Container. Perhaps this is because there are no comments anywhere.



  • You aren't completely consistent about use of parentheses vs. braces for initialization. For example, proxy's constructor uses parentheses where I believe it should use braces: proxy(sparse_array& sp, std::size_t index) : sp{sp}, index{index}



  • It's plausible that default_value should be exposed in the template as well, rather than relying on its type's default.



  • I'm not yet sold on auto method_name(params) -> result_type when result_type is a simple known type. It's too different from the result_type method_name(params) that I've been using for so many years.

Code Snippets

sparse_array<int> sp;
// ::: fill array here :::

for (int elt: sp)
    std::cout << elt << ", ";

// or better
std::ostream_iterator<int> out_it(std::cout, ", ");
std::copy(std::begin(sp), std::end(sp), out_it);
auto spot = &sp[4];
*(spot + 1) = 5;

Context

StackExchange Code Review Q#43643, answer score: 5

Revisions (0)

No revisions yet.