What is the type of decltype(this) in C++?

kccqzy picture kccqzy · Nov 16, 2013 · Viewed 7.3k times · Source

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: iterators and const_iterators. 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.

Answer

kccqzy picture kccqzy · Nov 16, 2013

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:
  — if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
  — otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;
  — otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
  — otherwise, decltype(e) is the type of e.
The operand of the decltype 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 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).   [ 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.