Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the compiler want a trailing return-type?

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.

like image 302
Jive Dadson Avatar asked Sep 29 '12 21:09

Jive Dadson


People also ask

Is return type necessary?

Every method in Java is declared with a return type and it is mandatory for all java methods. A return type may be a primitive type like int, float, double, a reference type or void type(returns nothing). The type of data returned by a method must be compatible with the return type specified by the method.

Can return type be auto?

In C++14, you can just use auto as a return type.

What is return type and parameter list in function header?

Some functions perform the desired operations without returning a value. In this case, the return_type is the keyword void. Function Name − This is the actual name of the function. The function name and the parameter list together constitute the function signature. Parameters − A parameter is like a placeholder.


1 Answers

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:

if that id-expression denotes a non-static data member and it appears in an unevaluated operand.

The expression in decltype(expr) is an unevaluated operand. Furthermore, 9.3.1 [class.mfct.non-static] paragraph 3 explains the situation where this is implicitly added to an expression:

When an id-expression (5.1) that is not part of a class member access syntax (5.2.5) and not used to form a pointer to member (5.3.1) is used in a member of class X in a context where this can be used (5.1.1), if name lookup (3.4) resolves the name in the id-expression to a non-static non-type member of some class C, and if either the id-expression is potentially evaluated or C is X or a base class of X, the id-expression is transformed into a class member access expression (5.2.5) using (*this) (9.3.2) as the postfix-expression to the left of the . operator.

The context in question is not "potentially evaluated" and there is no based involved. Thus, this isn't added and doesn't have to be in scope. Taken together, this means that the declaration

decltype(fwd.rbegin()) begin() const;

should be legal. It seems that using

decltype(static_cast<Reverser<Fwd> const*>(0)->fwd.rbegin()) begin() const;

is a work-around in cases where the compiler isn't correctly implemented.

like image 60
5 revs, 2 users 84% Avatar answered Oct 03 '22 07:10

5 revs, 2 users 84%