mardi 28 juin 2016

View class into a container

I'm trying to write a class View to serve as a view into another container, (a sparse matrix class, but that should be unimportant for the question).

View should contain references (e.g. std::reference_wrapper) to a selection of elements in the container, and have methods returning references to those elements, as well as an assignment operator making one block equal to another.

My problem is that I want View to be able to take values in addition to references: both be constructed from values as a non-reference instance to be used in assignments, and assign values to single elements in a reference instance.

An MVE of the code so far is:

#include <array>

template<typename T, size_t size>
class View
{

    private:
        std::array<T, size> _values;

    public:
        View(const std::array<T, size> & values)
            : _values{ values } { }
        // ----------
        View<T, size> & operator=(const View<T, size> & other)
        {
            for ( size_t i = 0; i < size; ++i ) {
                this->get(i) = other.get(i);
            }
            return *this;
        }
        // ----------
        T & get(size_t idx)
        {
            return _values.at(idx);
        }
        const T & get(size_t idx) const
        {
            return _values.at(idx);
        }

};

It can be used like this:

#include <functional>
#include <iostream>

int main()
{
    int values[5] = { 1, 2, 3, 4, 5 };

    View<int, 2> v1{
        {values[0], values[1]}
    };
    View<std::reference_wrapper<int>, 2> v2{
        {values[3], values[4]}
    };

    // WHAT WORKS

    v1.get(0) = 10;  // can assign to the non reference `View<int, size>`,
                     // works as intended

    v2.get(0) += 9;  // can increment through the reference wrappers,
                     // this also works as intended

    // WHAT THAT DOES NOT WORK

    // v2 = v1;  // nether of these work, as there is no conversion
    // v1 = v2;  // between `View<std::reference_wrapper<int>, size>`
                 // and `View<int, size>`. It is the first expression
                 // that is of most interest

    // v2.get(1) = 10;     // this doesn't work as the return is a
                           // `std::reference_wrapper<int>`, not a
                           // reference to an `int`
    v2.get(1).get() = 10;  // this works as a work-around to
                           // this problem, but it feels clunky, and it
                           // makes the interface between the two types
                           // different

    for ( size_t i = 0; i < 2; ++i ) {
        std::cout << v1.get(i) << " ";
    }
    std::cout << std::endl;
    for ( size_t i = 0; i < 5; ++i ) {
        std::cout << values[i] << " ";
    }
    std::cout << std::endl;
}

This should output:

10 2
1 2 3 13 10

I'm using clang++ to compile on Ubuntu 15.10.


So specifically,

  1. How should I implement the assignment operator to allow View<T, size> and View<std::reference_wrapper<T>, size> to be assigned to each other (or at least the former to be assigned to latter). Creating two versions

    View<T, size> & operator=(const View<T, size> & other);
    View<T, size> & operator=(
        const View<std::reference_wrapper<T>, size> & other);
    
    

    does not work, (as a View<std::reference_wrapper<T>, size> then would need a View<std::reference_wrapper<std::reference_wrapper<T> >, size> for the second overload).

  2. How can I write the get(size_t idx) methods such that the return is T & for bothView<T, size> and View<std::reference_wrapper<T>, size>?

I have a feeling this can be accomplished by using templates somehow, but I'm still quite new to template programming so I'm a bit lost.

Aucun commentaire:

Enregistrer un commentaire