簡體   English   中英

矩陣類的c ++自定義迭代器

[英]c++ custom iterator for matrix class

我有一個Matrix類,現在我想為它編寫一個迭代器。 我的班級看起來像這樣:

template <typename T>
class Matrix {
public:
vector<vector<T>> data;
Matrix(const vector<vector<T>>& d)
        : data(d) {
}
std::pair<size_t, size_t> size() const {
    std::pair<size_t, size_t> s;
    s.first = data.size();
    s.second = data[0].size();
    return s;
}

我想為它編寫一個迭代器。 但是我該怎么做呢? 我找到了以下代碼:

class iterator
    {
        public:
            typedef iterator self_type;
            typedef T value_type;
            typedef T& reference;
            typedef T* pointer;
            typedef std::forward_iterator_tag iterator_category;
            typedef int difference_type;
            iterator(pointer ptr) : ptr_(ptr) { }
            self_type operator++() { self_type i = *this; ptr_++; return i; }
            self_type operator++(int junk) { ptr_++; return *this; }
            reference operator*() { return *ptr_; }
            pointer operator->() { return ptr_; }
            bool operator==(const self_type& rhs) { return ptr_ == rhs.ptr_; }
            bool operator!=(const self_type& rhs) { return ptr_ != rhs.ptr_; }
        private:
            pointer ptr_;
    };

 iterator begin()
    {
        return iterator(data_);
    }

    iterator end()
    {
        return iterator(data_ + size_);
    }

我怎樣才能讓它一起工作? 我想迭代矩陣中的每個元素

這里的C ++解決方案沒有提升。 我還提供了一個完整且經過測試的源代碼示例。

使用MS Visual Studio 19編譯和測試源代碼。

第一個提示:我總是使用std::valarray進行矩陣計算。 請閱讀它。

解釋這個解決方案:

我們將使用int的向量向量來表示矩陣。 可以輕松訪問行。 它們是數據矩陣的第一維。 如果我們想要一個行的迭代器,我們簡單地返回一個向量的標准迭代器。 有了它,我們將立即擁有完整的功能。 簡單。

不幸的是,列不同。 它們是數據連續內存中的切片。 因此,我們將作為解決方案實現的是:為每個列創建一個向量,並在正確的位置引用數據。

這聽起來比較簡單,因為我們無法在C ++中的容器中存儲引用。 所以,要么使用std::reference_wrapper要么構建我們自己的引用包裝器。 我遇到了為取消引用std::reference_wrapper賦值並構建自己的值的問題。 已添加賦值運算符。

有了它,我們可以根據引用向量將迭代器返回到列。

可以添加許多其他功能。 當然,您可以將int替換為任何其他值或將其模板化。

我把一些測試代碼放在main中

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <tuple>
#include <sstream>
#include <numeric>


// Unfortunately the std::reference_wrapper does not work as expected.
// So we will build our own one
class IntRef
{
    // Here we will store the reference
    std::tuple<int&> t;
public:
    // Constructor. Take reference and store it in tuple
    IntRef(int&& intV) : t(intV) {}     

    // Assignment to the referenced value
    int operator =(const int i) { std::get<0>(t) = i; return i; }      

    // Explicit type cast to int&
    operator int& () { return std::get<0>(t); }                        

    // And, return the reference
    decltype(&std::get<0>(t)) operator&() { return &std::get<0>(t); }  
};


// Some definitions to make reading easier
using IntRefV = std::vector<IntRef>;
using MatrixCIterator = std::vector<IntRef>::iterator;
using Columns = std::vector<int>;
using MatrixRIterator = Columns::iterator;


// The matrix
class Matrix
{
public:
    // Constructor defines the matrix size
    Matrix(size_t numberOfRows, size_t numberOfColumns);

    // Iterators for rows are simple, becuase we have vectors of columns. Use unterlying iterator
    MatrixRIterator rowIterBegin(size_t row) { return data[row].begin(); }
    MatrixRIterator rowIterEnd(size_t row) { return data[row].end(); }  

    // Column iterator is complicated. Retzurn iterator to vevtor of references to column values
    MatrixCIterator columnIterBegin(size_t column) { return columnReferences[column].begin(); }
    MatrixCIterator columnIterEnd(size_t column) { return columnReferences[column].end(); }

    // Access data of matrix
    std::vector<int>& operator [] (const size_t row) { return data[row]; }

    // And, for debug purposes. Output all data
    friend std::ostream& operator << (std::ostream& os, const Matrix& m) {
        std::for_each(m.data.begin(), m.data.end(), [&os](const Columns& columns) {std::copy(columns.begin(), columns.end(), std::ostream_iterator<int>(os, " ")); std::cout << '\n'; });
        return os;
    }
protected:
    //The matrix, vector of vector of int
    std::vector<Columns> data; 

    // The references to columns in data
    std::vector<IntRefV> columnReferences{};    
};

// Constructor. Build basic matrix and then store references to columns in data 
Matrix::Matrix(size_t numberOfRows, size_t numberOfColumns) : data(numberOfRows, std::vector<int>(numberOfColumns)), columnReferences(numberOfColumns)
{
    for (size_t column = 0; column < numberOfColumns; ++column) 
        for (size_t row = 0; row < numberOfRows; ++row)
            columnReferences[column].emplace_back(IntRef(std::move(data[row][column]))); // Std::move creates a rvalue reference (needed for constructor, nothing will be moved)
}



// Some test data for the istream_iterator
std::istringstream testData("1 2 10");



// Test the matrix
int main()
{
    // Define a matrix with 3 rows and 4 columns
    Matrix matrix(3, 4);
    // Test 1: Fill all values in column 2 with 42
    for (MatrixCIterator ci = matrix.columnIterBegin(2); ci != matrix.columnIterEnd(2); ++ci) {
        *ci = 42;
    }
    std::cout << matrix << "Column 2 filled with 42\n\n";

    // Test 2: Read input from istream and copy put that in column 1
    std::copy_n(std::istream_iterator<int>(testData), 3, matrix.columnIterBegin(1));
    std::cout << matrix << "Column 1 filled with testData '"<< testData.str() << "'\n\n";

    // Test 3: Copy column 2 to cout (Print column 2)
    std::copy(matrix.columnIterBegin(2), matrix.columnIterEnd(2), std::ostream_iterator<int>(std::cout, " "));
    std::cout << "This is column 2\n\n";

    // Test 4: Sum up the first 2 values of column 1 and show result
    std::cout << "\nSum of first 2 values of column 1:  " << std::accumulate(matrix.columnIterBegin(1), matrix.columnIterBegin(1)+2, 0) << "\n\n";

    // Test 5: Fill all values in row 0 with 33
    std::for_each(matrix.rowIterBegin(0), matrix.rowIterEnd(0), [](int& i) { i = 33; });
    std::cout << matrix << "Row 0 filled with 33\n\n";

    return 0;
}

希望這能讓您了解它的工作原理。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM