以范围为参数

时间:2018-04-04 22:26:19

标签: c++

许多STL算法采用范围和参数。我有兴趣了解在构造函数中使用范围需要什么。

假设我正在创建一个Matrix类。构造函数看起来像什么? 我相信前向迭代器是最通用的。

#include <algorithm>
#include <iterator>
#include <vector>

using namespace std;

template <class T>
class Matrix
{
private:

    const size_t                m_order;
    std::vector<std::vector<T>> m_data;

public:

    Matrix(const size_t order, forward_iterator_tag begin, forward_iterator_tag end)
        :
        m_order(order)
      , m_data (order, std::vector<T>(order))
    {
        if (std::distance(begin, end) != order * order)
        {
            throw std::runtime_error("invalid params");
        }

        for (size_t currentRow = 0; currentRow < m_order; ++currentRow)
        {
            for (size_t currentColumn = 0; currentColumn < m_order; ++currentColumn)
            {
                m_data[row].push_back(*begin);

                if (++begin == end)
                {
                    throw std::runtime_error("invalid params");
                }
            }
        }
    }

    T GetElement(size_t row, size_t column) const
    {
        if (row > order || column > order)
        {
            throw std::runtime_error("invalid params");
        }

        return m_data[row][column];
    }
};


//------------------------------------------------------------------------------
// Finds all permutations of the distinct numbers that make up a matrix of order n, from 1 to n^2
std::vector<std::vector<size_t>> GetAllPermutations(size_t order)
{
    std::vector<std::vector<size_t>> result;

    // It is important than we have these sorted least to greatest to start, in order to use the next_permutation algorithm
    std::vector<size_t> elements;
    for(size_t currentNumber = 1; currentNumber <= order * order; ++currentNumber)
    {
        elements.push_back(currentNumber);
    }

    do
    {
        // Store the current permutation
        result.push_back(elements);

    } while (std::next_permutation(elements.begin(), elements.end()));

    return result;
}

int main()
{
    const size_t order = 3;
    std::vector<std::vector<size_t>> permutations = GetAllPermutations(order);

    Matrix<size_t> matrix(order, permutations.begin(), permutations.end());

    return 0;
}

但迭代器显然不是正确的类型。我该用什么?

3 个答案:

答案 0 :(得分:3)

您声明构造函数的迭代器参数是std::forward_iterator_tag的实例,它是iterator tag,而不是实际的迭代器。

Iterator标记用于将模板特化限制为特定类型的迭代器。

例如,假设算法需要随机访问迭代器。如果向其传递非随机访问迭代器,则会产生编译器错误。

或者,给定算法的多次重载可以使用迭代器标记来限制它们接受的迭代器的类型,因此在传递不同类型的迭代器时它们可以使用不同的逻辑/优化。

在你的情况下,要做你想做的事,你需要改变构造函数以获得迭代器类型的模板参数(就像STL算法一样):

template<class InputIt>
Matrix(const size_t order, InputIt first, InputIt last)

现在,你可以传入你想要的任何类型的迭代器(只要取消引用迭代器就会产生一个与你的MatrixT模板参数兼容的值,因为那样是你的构造函数体如何使用迭代器值。)

或者,如果你想确保只传递正向输入迭代器的迭代器,而不是任何其他类型的迭代器,你可以做更多这样的事情:

private:
    template<class InputIt>
    Matrix(const size_t order, InputIt first, InputIt last, std::forward_iterator_tag)
    {
        // initialization here ...
    }

public:
    template<class InputIt>
    Matrix(const size_t order, InputIt first, InputIt last)
        : Matrix(order, first, last, typename std::iterator_traits<InputIt>::iterator_category())
    {
    }

仅当InputIt是迭代器类型时,代码才会编译,std::iterator_traits特化提供iterator_category std::forward_iterator_tag或后代。

这种方法通常称为Tag Dispatching

答案 1 :(得分:0)

forward_iterator_tagdefines the category of an iterator的标记。您无法从该参数初始化迭代器。

迭代器可以是指针,类,整数 - 基本上任何满足iterator categories的对象。显然,您无法预测在一般情况下将传递的迭代器类型,这就是为什么让函数使用模板推导出迭代器是很常见的。

template<class InputIt>
void foo(InputIt begin, InputIt end);

这样您就不需要知道迭代器的类型,并且可以统一的方式处理所有可能的迭代器类型。

答案 2 :(得分:0)

你通过使用矢量向量使你的生活变得艰难。 你不需要它,它会导致内存碎片化。

template<class T>
class Matrix
{
private:
    std::size_t m_order;
    std::vector<T> m_data;

public:
    template <class InputIterator>
    Matrix(std::size_t order, 
           InputIterator begin, InputIterator end)
    : m_order(order)
    {
        if (std::distance(begin, end) > order * order) {
            throw std::runtime_error("invalid params");
        }
        m_data.reserve(order * order);
        std::copy(begin, end, std::back_inserter(m_data));
        m_data.resize(order * order);
    }

    T GetElement(size_t row, size_t column) const
    {
        checkIndexes(row, column);
        return m_data[row + column * m_order];
    }

    void SetElement(size_t row, size_t column, T value)
    {
        checkIndexes(row, column);
        m_data[row + column * m_order] = value;
    }

private:
    void checkIndexes(size_t row, size_t column) const {
        if (row > order || column > order) {
            throw std::runtime_error("invalid params");
        }
    }
… … …