c++ - Why does my custom iterator require a call operator in range based for loops? -
link mcve.
we define matrix iterable both rows , columns. here's implementation of row-wise iterator:
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; };
iterating on our matrix implemented using range
object define follows:
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); }
this our usage code:
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 = 0; < rows * cols; ++i) mat[i] = (float)i; auto rowrange = make_row_range(mat, rows, cols); // loop works 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; } // loop produces confusing compiler errors std::cout << "range based" << std::endl; for(auto row : rowrange) { // line 42 // row of type float* std::cout << "start of row: " << row[0] << std::endl; } return 0; }
i compiled above mcve , got following compiler errors:
visual studio 2013 (all on line 42):
error c2064: term not evaluate function taking 0 arguments error c3536: '$s2': cannot used before initialized error c3536: '$s3': cannot used before initialized error c2100: illegal indirection error c2440: 'initializing' : cannot convert 'int' 'float *'
gcc 5.1 (on line 42):
error: no match call '(rowiterator<float>) ()'
clang 3.7.0 (on line 42):
error: type 'rowiterator<float>' not provide call operator note: when looking 'begin' function range expression of type 'details::range<rowiterator<float> >'
all compilers searching call operator. why? as understand, above iterator provides minimal interface ranged loops and works when using syntactical equivalence code cppreference.com.
while writing question came solution (rubber debugging?): compiler first checks members range::begin
, range::end
, tries invoke leading missing call operator. none of tested compilers indicated in error messages[1]. fix rename them:
namespace range { template<class iterator> struct range { // "begin" , "end" have ultra-special meaning in 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; } }
the requirements on class range
defined (source: cppreference.com, emphasis mine):
begin_expr , end_expr defined follows:
1 if range_expression expression of array type, begin_expr
__range
, end_expr(__range + __bound)
,__bound
number of elements in array (if array has unknown size or of incomplete type, program ill-formed)2 if range_expression expression of class type c has member named
begin
and/or member namedend
(regardless of type or accessibility of such member), begin_expr__range.begin()
, end_expr__range.end()
;3 otherwise, begin_expr
begin(__range)
, end_exprend(__range)
, found via argument-dependent lookup (non-adl lookup not performed).
[1]: clang came close, though message ambiguous: thought (adl) looking details::begin(range)
instead looking straight @ range::begin
.
Comments
Post a Comment