gpt4 book ai didi

c++ - 为什么我的自定义迭代器需要基于范围的 for 循环调用运算符?

转载 作者:行者123 更新时间:2023-11-28 05:38:10 25 4
gpt4 key购买 nike

链接到 mcve .

我们将矩阵定义为可按行和按列迭代。这是按行迭代器的实现:

template<class Real>
class RowIterator {
public:
RowIterator() { }
RowIterator(Real* begin, size_t rows, size_t cols) : begin(begin), rows(rows), cols(cols) { }

Real* operator*() const { return begin; }
Real& operator[](size_t col) const { return begin[col]; }

bool operator!=(const RowIterator& it) const { return begin != it.begin; }
RowIterator& operator++() { begin += cols; --rows; return *this; }

private:
Real* begin;
size_t rows, cols;
};

迭代我们的矩阵是使用 Range 对象实现的,定义如下:

namespace details
{

template<class Iterator>
struct Range {
Iterator begin, end;
Range() { }
Range(Iterator begin, Iterator end) : begin(begin), end(end) { }
};

template<class Iterator>
Iterator begin(const Range<Iterator>& range) { return range.begin; }
template<class Iterator>
Iterator end(const Range<Iterator>& range) { return range.end; }

}

using details::Range;
template<class Iterator>
Range<Iterator> make_range(Iterator begin, Iterator end) { return Range<Iterator>(begin, end); }

这基本上是我们的使用代码:

Range<RowIterator<float>> make_row_range(float* mat, size_t rows, size_t cols) {
return make_range(
RowIterator<float>(mat, rows, cols),
RowIterator<float>(mat + rows * cols, 0, cols));
}

int main() {
size_t rows = 4, cols = 6;
float* mat = new float[rows * cols];
for(size_t i = 0; i < rows * cols; ++i)
mat[i] = (float)i;
auto rowrange = make_row_range(mat, rows, cols);

// this loop works as expected
std::cout << "begin, end" << std::endl;
for(auto b = begin(rowrange), e = end(rowrange); b != e; ++b) {
// using RowIterator<T>::operator[](size_t)
std::cout << "start of row: " << b[0] << std::endl;
}

// this loop produces confusing compiler errors
std::cout << "range based" << std::endl;
for(auto row : rowrange) { // this is line 42
// row is of type float*
std::cout << "start of row: " << row[0] << std::endl;
}
return 0;
}

我编译了上面的 MCVE 并得到了以下编译器错误:

  • Visual Studio 2013(全部在第 42 行):

    error C2064: term does not evaluate to a function taking 0 arguments
    error C3536: '$S2': cannot be used before it is initialized
    error C3536: '$S3': cannot be used before it is initialized
    error C2100: illegal indirection
    error C2440: 'initializing' : cannot convert from 'int' to 'float *'
  • GCC 5.1(第 42 行):

    error: no match for call to '(RowIterator<float>) ()'
  • Clang 3.7.0(第 42 行):

    error: type 'RowIterator<float>' does not provide a call operator
    note: when looking up 'begin' function for range expression of type 'details::Range<RowIterator<float> >'

所有编译器都在搜索调用运算符。 为什么? As I understand , 上面的迭代器为范围循环提供了最小的接口(interface) and it works when using the syntactical equivalence code from cppreference.com .

最佳答案

在写这个问题时我想到了解决方案(rubber SO debugging?):编译器首先检查成员 Range::beginRange::end 并尝试调用导致缺少调用运算符的那些。没有一个经过测试的编译器在它们的错误消息中明确指出这一点 [1]。解决方法是简单地重命名它们:

namespace range
{

template<class Iterator>
struct Range {
// "begin" and "end" have ultra-special meaning in this context!!!
Iterator range_begin, range_end;
Range() { }
Range(Iterator begin, Iterator end) : range_begin(begin), range_end(end) { }
};

template<class Iterator>
Iterator begin(const Range<Iterator>& range) { return range.range_begin; }
template<class Iterator>
Iterator end(const Range<Iterator>& range) { return range.range_end; }

}

Range 类的要求定义明确(来源:cppreference.com,强调我的):

begin_expr and end_expr are defined as follows:

1 If range_expression is an expression of array type, then begin_expr is __range and end_expr is (__range + __bound), where __bound is the number of elements in the array (if the array has unknown size or is of an incomplete type, the program is ill-formed)

2 If range_expression is an expression of a class type C that has a member named begin and/or a member named end (regardless of the type or accessibility of such member), then begin_expr is __range.begin() and end_expr is __range.end();

3 Otherwise, begin_expr is begin(__range) and end_expr is end(__range), which are found via argument-dependent lookup (non-ADL lookup is not performed).

[1]:Clang 实际上接近了,尽管它的信息也含糊不清:我认为它是 (adl) 查找 details::begin(Range) 而不是直接查看 范围::开始

关于c++ - 为什么我的自定义迭代器需要基于范围的 for 循环调用运算符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37762724/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com