I was playing around with an adapter for using range-based for-loops to iterate in reverse. (I did not know about the boost adapter (“adaptor”) for that purpose. I am a big believer in not re-inventing the wheel if it’s a free wheel I have already downloaded.)
What puzzles me is why VC++ 2012 is not happy unless I use trailing return-types in the code that follows:
#include <string>
#include <iostream>
template<class Fwd>
struct Reverser {
const Fwd &fwd;
Reverser<Fwd>(const Fwd &fwd_): fwd(fwd_) {}
auto begin() -> decltype(fwd.rbegin()) const { return fwd.rbegin(); }
auto end() -> decltype(fwd.rend()) const { return fwd.rend(); }
};
template<class Fwd>
Reverser<Fwd> reverse(const Fwd &fwd) { return Reverser<Fwd>(fwd); }
int main() {
using namespace std;
const string str = ".dlrow olleH";
for(char c: reverse(str)) cout << c;
cout << endl;
}
When I tried the following, I got the errors, “error C2100: illegal indirection,” and “error C2228: left of ‘.rbegin’ must have class/struct/union”. What am I missing?
template<class Fwd>
struct Reverser {
const Fwd &fwd;
Reverser<Fwd>(const Fwd &fwd_): fwd(fwd_) {}
decltype(fwd.rbegin()) begin() const { return fwd.rbegin(); }
decltype(fwd.rend()) end() const { return fwd.rend(); }
};
UPDATE: In light of the discussion about a “this” pointer, I tried another tack. Look Ma, no this! And it compiles fine. I do believe that, rightly or wrongly, VC++ is not aware of that this.
template<class Fwd>
struct Reverser {
const Fwd &fwd;
Reverser<Fwd>(const Fwd &fwd_): fwd(fwd_) {}
decltype(((const Fwd*)0)->rbegin()) begin() const { return fwd.rbegin(); }
decltype(((const Fwd*)0)->rend()) end() const { return fwd.rend(); }
};
UPDATE 2: Submitted to MS: https://connect.microsoft.com/VisualStudio/feedback/details/765455/vc-2012-compiler-refuses-decltype-return-spec-for-member-function
UPDATE 3: This problem is fixed as of VC++ 2015. Thanks, Microsoft person.
NOTE FROM OP: VC++ was buggy. VC++ 2015 accepts the code correctly.
My original answer that the code that VC++ rejected is disallowed in the standard actually was wrong: It is allowed as Johannes Schaub pointed out: in 5.1 [expr.prim.general] paragraph 12 it is described where an id-expression denoting a non-static data member or a non-static member function can be used. In particular, the last bullet states:
The expression in
decltype(expr)is an unevaluated operand. Furthermore, 9.3.1 [class.mfct.non-static] paragraph 3 explains the situation wherethisis implicitly added to an expression:The context in question is not “potentially evaluated” and there is no based involved. Thus,
thisisn’t added and doesn’t have to be in scope. Taken together, this means that the declarationshould be legal. It seems that using
is a work-around in cases where the compiler isn’t correctly implemented.