【问题标题】:Eclipse CDT: cannot resolve 'begin' and 'end' symbols in foreach-loopEclipse CDT:无法解析 foreach 循环中的“开始”和“结束”符号
【发布时间】:2018-08-20 09:18:40
【问题描述】:

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

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 中的(已知)错误?

【问题讨论】:

    标签: c++11 foreach eclipse-cdt


    【解决方案1】:

    如果我们看一下begin的定义

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

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

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

    您的代码还有其他可能的缺陷,特别是因为do_something 按值返回。这意味着对do_something 的每次调用都将返回一个单独的对象,如果多次调用do_something,您将拥有具有不同数据的不同对象。

    另请注意,std::iterator 在 C++17 标准中已被弃用。如果你想要通用类型,你应该专门化std::iterator_traits

    【讨论】:

    • 谢谢,确实有效!但是,我仍然想知道为什么它可以正确编译(在 G++ 5 和 MSVC 中)。也感谢额外的 cmets!
    • @0xfull 我不知道为什么它显然适用于 GCC 5,但 MSVC++ 对语言有一个非标准扩展,允许将临时对象绑定到非常量引用。
    • 代码不仅可以用 GCC 5 编译,还可以用较新的版本和 Clang 编译。我相信它符合要求,并且 OP 在 Eclipse CDT 中发现了一个错误。
    • 请看我的回答,我解释了为什么这段代码实际上格式正确。
    【解决方案2】:

    有问题的代码实际上是符合要求的。它与最新版本的 gcc 和 clang 一起编译。

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

    [stmt.ranged] p1 说:

    基于范围的 for 语句

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

    等价于

    {
        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 &amp;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,很快就会修复。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-18
      • 2012-06-03
      • 1970-01-01
      • 2013-10-27
      • 2013-12-01
      相关资源
      最近更新 更多