Eclipse CDT:无法解析 foreach 循环中的 'begin' 和 'end' 符号

Eclipse CDT: cannot resolve 'begin' and 'end' symbols in foreach-loop

每当我想直接迭代函数返回的数据时,eclipse CDT 的索引器无法正确识别 foreach 循环中所需的 'end' 和 'begin' 符号。当我第一次将结果放入临时变量时它起作用了。

MWE:

#include "mwe.h"

int main(int argc, char **argv) {
    auto tmp = do_something();
    for(auto &x : tmp){ } //Working
    for(auto &x : do_something()){ } //Symbol 'end'/'begin' could not be resolved
}

mwe.h:

#include<iterator>

class X { };
class Handle { };
class MyIterator: public std::iterator<X, std::input_iterator_tag> {
public:
    explicit MyIterator(Handle &iter) : iter_(&iter) { }
    MyIterator() { }
    MyIterator &operator++() { return *this; }
    MyIterator operator++(int) { return *this;  }
    X &operator*() { return x;  }
    X *operator->() { return &**this; }
    friend bool operator==(MyIterator a, MyIterator b) { return true; }
    friend bool operator!=(MyIterator a, MyIterator b) { return false; }
private:
    Handle *iter_;
    X x;
};

inline MyIterator begin(Handle &it) { return MyIterator(it); }
inline MyIterator end(Handle &) { return MyIterator(); }

Handle do_something() { return Handle(); }

代码编译并没有错误,只有索引器告诉我它没有找到符号。也就是说,解决这个问题并不是真的有必要,但它很烦人。

附加说明:我已经在 CDT 中检查了许多其他关于索引器的问题,但答案没有解决我的问题:

Eclipse CDT Indexer does not fully recognize c++11

Eclipse CDT: Symbol 'cout' could not be resolved

https://www.eclipse.org/forums/index.php/t/636348/

代码有问题还是 CDT 中的(已知)错误?

如果我们看一下begin

的定义
inline MyIterator begin(Handle &it) { return MyIterator(it); }

我们看到它引用了一个非常量对象。这意味着您不能将临时对象传递给函数。就像直接使用 do_something() 时发生的情况一样。快速的解决方案是添加一个采用 右值引用 的重载:

inline MyIterator begin(Handle &&it) { return MyIterator(it); }
//                             ^^
//     Note double ampersand here

虽然您的代码还有其他可能的陷阱,特别是因为 do_something returns 按值计算。这意味着每次调用 do_something 都会 return 一个单独的对象,如果 do_something 被调用不止一次,您将拥有具有不同数据的不同对象。

另请注意,如果您需要通用类型,请改用 std::iterator have been deprecated in the C++17 standard. You should specialize std::iterator_traits

有问题的代码实际上是符合规范的。它使用最新版本的 gcc 和 clang 编译。

另一个答案的缺陷在于它没有考虑编译器对基于范围的 for 循环执行的重写。

[stmt.ranged] p1 说:

The range-based for statement

for ( init-statement_opt for-range-declaration : for-range-initializer) statement

is equivalent to

{
    init-statement_opt
    auto &&__range = for-range-initializer ;
    auto __begin = begin-expr ;
    auto __end = end-expr ;
    for ( ; __begin != __end; ++__begin ) {
        for-range-declaration = *__begin;
        statement
    }
}

因此,在这种情况下,for(auto &x : do_something()){ } 被重写为:

{
    auto &&__range = do_something();
    auto __begin = begin(__range);
    auto __end = end(__range);
    for ( ; __begin != __end; ++__begin ) {
        auto &x = *__begin;
    }
}

请注意,调用 begin()end() 的实际参数不是表达式 do_something() 本身,而是一个隐藏的(名义上的)变量 __range。由于 __range 是一个命名变量,它在表达式上下文中是一个 左值 ,并且 begin()end() 的左值引用参数将成功绑定到它.


Eclipse CDT 在此代码上给出的错误是一个错误,我刚刚 filed,很快就会修复。