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.
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.