簡體   English   中英

為什么我的自定義迭代器需要基於范圍的for循環中的調用運算符?

[英]Why does my custom iterator require a call operator in range based for loops?

鏈接到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> >' 

所有編譯器都在搜索調用運算符。 為什么? 據我了解 ,上述迭代器為范圍循環提供了最小的接口, 並且在使用來自cppreference.com的語法等效代碼時可以使用

在寫這個問題時,我想出了解決方案( 橡膠SO調試嗎?):編譯器首先檢查成員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_exprend_expr定義如下:

1如果range_expression是數組類型的表達式,則begin_expr__rangeend_expr(__range + __bound) ,其中__bound是數組中元素的數量(如果數組的大小未知或類型不完整,則程序為格式不正確)

2如果range_expression是類C類型的表達式,並且具有名為begin的成員和/或名為end的成員(不管此類成員的類型或可訪問性),則begin_expr__range.begin()end_expr__range.end() ;

3否則, begin_exprbegin(__range)end_exprend(__range) ,這是通過與參數相關的查找找到的(不執行非ADL查找)。

[1]:Clang實際上接近了,盡管它的消息是模棱兩可的:我以為是(adl)查找details::begin(Range)而不是它直接看了Range::begin

暫無
暫無

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

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