Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inside of a class, why `auto b() -> decltype(a()) {}` works, but `decltype(a()) b() {}` does not?

Consider following code: (Ideone)

struct S
{
    int a() {return 0;}
    decltype(a()) b() {return 1;}
};

It gives me following error:

error: cannot call member function 'int S::a()' without object


On the other hand, this code compiles fine: (Ideone)

struct S
{
    int a() {return 0;}
    auto b() -> decltype(a()) {return 1;}
};


Why one example works, but another fails to compile?

Is compiler behavior fully correct in both examples?

If compiler is correct, then why the standard mandates such strange behavior?

like image 562
HolyBlackCat Avatar asked May 04 '16 22:05

HolyBlackCat


People also ask

What is the difference between auto and decltype in C++?

auto is a keyword in C++11 and later that is used for automatic type deduction. The decltype type specifier yields the type of a specified expression. Unlike auto that deduces types based on values being assigned to the variable, decltype deduces the type from an expression passed to it.

What does decltype in c++?

In the C++ programming language, decltype is a keyword used to query the type of an expression. Introduced in C++11, its primary intended use is in generic programming, where it is often difficult, or even impossible, to express types that depend on template parameters.

Why to use decltype?

The decltype type specifier yields the type of a specified expression. The decltype type specifier, together with the auto keyword, is useful primarily to developers who write template libraries. Use auto and decltype to declare a template function whose return type depends on the types of its template arguments.

What does decltype stand for?

Decltype keyword in C++ Decltype stands for declared type of an entity or the type of an expression. It lets you extract the type from the variable so decltype is sort of an operator that evaluates the type of passed expression. SYNTAX : decltype( expression )


2 Answers

Since a is a non-static member function, a() is interpreted as (*this).a(). Quoting in part from [expr.prim.general]/3,

If a declaration declares a member function or member function template of a class X, the expression this is a prvalue of type “pointer to cv-qualifier-seq X” between the optional cv-qualifer-seq and the end of the function-definition, member-declarator, or declarator. It shall not appear before the optional cv-qualifier-seq and it shall not appear within the declaration of a static member function (although its type and value category are defined within a static member function as they are within a non-static member function).

The trailing-return-type comes after the optional cv-qualifier-seq (omitted in your examples, since S::b is not cv-qualified) so this can appear there, but it cannot appear before.

like image 119
Brian Bi Avatar answered Sep 28 '22 04:09

Brian Bi


A few additions to @Brian's answer:

  1. In the first example, the a() is not transformed to (*this).a(). That transformation is specified in [class.mfct.non-static]/3 and only takes place "in a context where this can be used". Without this transformation, the code is then ill-formed for violating [expr.prim.id]/2:

    An id-expression that denotes a non-static data member or non-static member function of a class can only be used:

    • as part of a class member access ([expr.ref]) in which the object expression refers to the member's class63 or a class derived from that class, or

    • to form a pointer to member ([expr.unary.op]), or

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

    by using the id-expression a, which denotes a non-static member function, outside the allowed contexts.

  2. The fact that the transformation to class-member access doesn't take place is important because it makes the following code valid:

    struct A {
        int a;
        decltype(a) b();
    };
    

    If decltype(a) above were transformed into decltype((*this).a), then the code would be ill-formed.

  3. *this has a special exemption from the usual rule that the object in a class member access must have complete type ([expr.prim.this]/2):

    Unlike the object expression in other contexts, *this is not required to be of complete type for purposes of class member access ([expr.ref]) outside the member function body.

like image 44
T.C. Avatar answered Sep 28 '22 03:09

T.C.