将类视图放入容器中

时间:2016-06-28 09:31:34

标签: c++ templates c++11 reference

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

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

我的问题是我希望View能够获取除引用之外的值:都可以从值构造为要在赋值中使用的非引用实例,并将值分配给a中的单个元素参考实例。

到目前为止,代码的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)方法,使T &View<T, size>的回报为View<std::reference_wrapper<T>, size>
  3. 我有一种感觉,这可以通过某种方式使用模板来实现,但我仍然是模板编程的新手,所以我有点迷失。

1 个答案:

答案 0 :(得分:2)

以下是get()T&的{​​{1}}返回T的方法:

std::reference_wrapper<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_typeT获取T,然后您只需将std::reference_wrapper<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); } }; 标准库中的大量内容,而无需重新定义任何内容。