将视图类放入容器中

View class into a container

本文关键字:视图      更新时间:2023-10-16

我试图写一个类View作为另一个容器的视图,(一个稀疏矩阵类,但这应该是不重要的问题)。

View应该包含对容器中元素选择的引用(例如std::reference_wrapper),并具有返回对这些元素的引用的方法,以及使一个块等于另一个块的赋值操作符。

我的问题是,我希望View能够接受值除了引用:两者都是从值构造为非引用实例中使用的赋值,并将值赋给引用实例中的单个元素。

目前为止代码的MVE是:

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

可以这样使用:

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

应该输出:

10 2
1 2 3 13 10

我用clang++在Ubuntu 15.10上编译。


那么特别,

  1. 我应该如何实现赋值运算符,以允许View<T, size>View<std::reference_wrapper<T>, size>被分配给对方(或至少前者被分配给后者)。创建两个版本

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

    不工作,(因为View<std::reference_wrapper<T>, size>然后需要View<std::reference_wrapper<std::reference_wrapper<T> >, size>进行第二次过载)。

  2. 如何编写get(size_t idx)方法,使View<T, size>View<std::reference_wrapper<T>, size>返回T & ?

我有一种感觉,这可以通过使用模板来完成,但我对模板编程仍然很陌生,所以我有点迷路了。

get()Tstd::reference_wrapper<T>返回T&:

template <typename T>
struct get_value_type {
    using type = T;
};
template <typename T>
struct get_value_type<std::reference_wrapper<T>> {
    using type = T;
};
template<typename T, size_t size>
class View {
    using value_type = typename get_value_type<T>::type;
    value_type & get(size_t idx) {
        return _values.at(idx);
    }
    const value_type & get(size_t idx) const {
        return _values.at(idx);
    }
};

get_value_type模板帮助我们从Tstd::reference_wrapper<T>中获得T,然后您只需将get()的返回类型更改为value_type,并且由于std::reference_wrapper<T>可以隐式转换为T&,因此它可以工作。

现在您可以访问value_type,您可以使用它来创建您的两个operator=:

View& operator= (const View<value_type, size> & other) {
    for (size_t i = 0; i < size; ++i) {
        this->get(i) = other.get(i);
    }
    return *this;
}
View& operator=(const View<std::reference_wrapper<value_type>, size> & other) {
    for (size_t i = 0; i < size; ++i) {
        this->get(i) = other.get(i);
    }
    return *this;
}

如果你想允许从不同的视图分配(例如int视图到double视图),你可以使用模板化版本:

template <typename U>
View<T, size> & operator=(const View<U, size> & other) {
    for (size_t i = 0; i < size; ++i) {
        this->get(i) = other.get(i);
    }
    return *this;
}

一个可能有点偏离主题的小添加,但您可以从std::array继承std::array属性,如以下所示:

template<typename T, size_t Size>
struct View: public std::array<T, Size> {
    using array_type = std::array<T, Size>;
    using value_type = typename get_value_type<T>::type;
    View (std::array<T, Size> const& values) : array_type (values) { }
    View& operator=(const View<value_type, Size> & other) {
        for (size_t i = 0; i < Size; ++i) {
            (*this)[i] = other[i];
        }
        return *this;
    }
    View& operator=(const View<std::reference_wrapper<value_type>, Size> & other) {
        for (size_t i = 0; i < Size; ++i) {
            (*this)[i] = other[i];
        }
        return *this;
    } 
    value_type & operator[](size_t idx) {
        return array_type::operator[](idx);
    }

    const value_type & operator[](size_t idx) const {
        return array_type::operator[](idx);
    }
};

这将允许您在View上使用标准库中的许多内容,而无需重新定义任何内容。