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?
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.
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.
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.
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 )
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 expressionthis
is a prvalue of type “pointer to cv-qualifier-seqX
” 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.
A few additions to @Brian's answer:
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.
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.
*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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With