Apparently clang thinks decltype(this)
is a pointer to the cv-qualified class, while gcc thinks it is a const reference to a pointer to the cv-qualified class. GCC only thinks decltype(&*this)
is a pointer to the cv-qualified class. This has some implications when it is used as the typename for a template. Consider a hypothetical example:
template<typename T> class MyContainer { /* ... */ template<typename ContainerPtr> class MyIterator { ContainerPtr container; /* ... */ }; auto cbegin() const -> MyIterator<decltype(&*this)> { return { /* ... */ }; } auto cend() const -> MyIterator<decltype(this)> { return { /* ... */ }; } };
In this example, one implements a custom container of T
. Being a container, it supports iterators. In fact, two kinds of iterators: iterator
s and const_iterator
s. It would not make sense to duplicate the code for these two, so one could write a template iterator class, taking either a pointer to the original class MyContainer<T> *
or a pointer to the const version MyContainer<T> const *
.
When cbegin
and cend
are used together, gcc errors out, saying it deduced conflicting types, while clang just works fine.
If the expression parameter is a call to a function or an overloaded operator function, decltype(expression) is the return type of the function. Parentheses around an overloaded operator are ignored. If the expression parameter is an rvalue, decltype(expression) is the type of expression.
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 )
decltype returnsIf what we pass to decltype is the name of a variable (e.g. decltype(x) above) or function or denotes a member of an object ( decltype x.i ), then the result is the type of whatever this refers to. As the example of decltype(y) above shows, this includes reference, const and volatile specifiers.
decltype(auto) is primarily useful for deducing the return type of forwarding functions and similar wrappers, where you want the type to exactly “track” some expression you're invoking.
this
is a prvalue, so decltype(this)
should always be plain X*
(or X cv*
/ cv X*
). The addition of const&
seems to be a bug in GCC (tested with g++ 4.8.1), which happens only for a class template (not for a "plain" class) and only inside the trailing return type (not inside the body of the member function): demo. This seems to be fixed in GCC 4.9 (experimental), you can test here.
Okay, here is what I found in the standard (N3337) though:
7.1.6.2 Simple type specifiers [dcl.type.simple]
4 The type denoted by
decltype(e)
is defined as follows:
— ife
is an unparenthesized id-expression or an unparenthesized class member access (5.2.5),decltype(e)
is the type of the entity named bye
. If there is no such entity, or ife
names a set of overloaded functions, the program is ill-formed;
— otherwise, ife
is an xvalue,decltype(e)
isT&&
, whereT
is the type ofe
;
— otherwise, ife
is an lvalue,decltype(e)
isT&
, whereT
is the type ofe
;
— otherwise,decltype(e)
is the type ofe
.
The operand of thedecltype
specifier is an unevaluated operand (Clause 5).
and
5.1.1 General [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). [ Note: this is because declaration matching does not occur until the complete declarator is known. — end note ] Unlike the object expression in other contexts,*this
is not required to be of complete type for purposes of class member access (5.2.5) outside the member function body. [ Note: only class members declared prior to the declaration are visible. — end note ]
The previous reference to §9.3.2 is an error, since that deals with the body of a member function, as pointed out below in a comment by MWid.
9.3.2 The `this` pointer [class.this] 1 In the body of a non-static (9.3) member function, the keyword `this` is a prvalue expression whose value is the address of the object for which the function is called. The type of `this` in a member function of a class `X` is `X*`. If the member function is declared `const`, the type of `this` is `const X*`, if the member function is declared `volatile`, the type of `this` is `volatile X*`, and if the member function is declared `const volatile`, the type of `this` is `const volatile X*`.
So it looks like gcc is wrong.
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