1

The indexer of eclipse CDT does not properly recognize the 'end' and 'begin' symbols needed in a foreach loop whenever I want to directly iterate over data returned by a function. It works when I first put the result into a temporary variable.

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(); }

The code compiles and works with no errors, only the indexer tells me that it does not find the symbols. That said, fixing the problem is not really necessary but it is quite annoying.

Additional note: I already checked many other Questions regarding the Indexer in CDT but the answers didn't fix my problem:

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/

Is there a problem with the code or is it a (known) bug in CDT?

0xfull
  • 33
  • 6

2 Answers2

1

If we look at the definition of begin

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

we see that it takes a reference to a non-constant object. That means you can not pass temporary objects to the function. Like what happens when using do_something() directly. The quick solution is to add an overload that take an rvalue reference instead:

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

There are other possible pitfalls with your code though, especially since do_something returns by value. That means each call to do_something will return a separate object, and if do_something is called more than once you will have different objects with different data.

Also note that std::iterator have been deprecated in the C++17 standard. You should specialize std::iterator_traits instead if you want the common types.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Thanks, that does work! However, I am still wondering why it compiles properly (both in G++ 5 and MSVC). Also thanks for the additional comments! – 0xfull Aug 20 '18 at 11:11
  • @0xfull I don't know why it apparently worked on GCC 5, but MSVC++ have a non-standard extension to the language that allows temporary objects to be bound to non-const references. – Some programmer dude Aug 20 '18 at 11:28
  • The code compiles not only with GCC 5, but also newer versions, and Clang. I believe it's conforming, and the OP has found a bug in Eclipse CDT. – HighCommander4 Aug 24 '18 at 18:26
  • Please see my answer where I explain why this code is actually well-formed. – HighCommander4 Sep 03 '18 at 03:59
0

The code in question is actually conforming. It compiles with recent versions of gcc and clang.

The flaw in the other answer is that it does not take into account the rewrite that the compiler performs on the range-based for loop.

[stmt.ranged] p1 says:

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
    }
}

So, in this case, for(auto &x : do_something()){ } is rewritten to:

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

Notice that the actual arguments to the calls to begin() and end() are not the expression do_something() itself, but a hidden (notional) variable __range. Since __range is a named variable, it's an lvalue in expression context, and the lvalue reference parameter of begin() and end() will bind to it successfully.


That Eclipse CDT gives an error on this code is a bug, which I've just filed, and will fix shortly.

HighCommander4
  • 50,428
  • 24
  • 122
  • 194