31

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.

kccqzy
  • 1,538
  • 1
  • 14
  • 22
  • 2
    Evaluating `this` gives an rvalue. You cannot, for example, take the address, as in `&this`. If `this` were a const-reference, you could take the address. – Kerrek SB Nov 16 '13 at 17:14
  • `this` is a prvalue of type `X cv*`, where `cv` corresponds to the `cv` in `R my_foo() cv;`. Are you sure Clang thinks that `this` is a cv-qualified pointer, and not a pointer to cv-qualified `X`? – Xeo Nov 16 '13 at 17:19
  • @Xeo, sorry, it should be a pointer to cv-qualified class. – kccqzy Nov 16 '13 at 17:23

2 Answers2

18

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.

Community
  • 1
  • 1
kccqzy
  • 1,538
  • 1
  • 14
  • 22
  • 1
    Note: Your original quote seemed to come from an old draft (older than [N3242](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf), the last public draft before standardization). In the actual C++11 Standard the final wording of 7.1.6.2/4 is *slightly different* (the draft closest to the Standard is [N3337](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf), which is basically the C++11 Standard + [editorial fixes](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3338.html)). I have thus suggested an edit. But the difference has no effect on the question. – gx_ Nov 16 '13 at 18:59
  • 1
    This answer doesn't apply to the question! `this` is **not** used in the body of a function and hence §9.3.2 has nothing to do with the question. The relevant part of the spec is §5.1.1/3. But anyway, it's a bug in gcc < 4.9. – MWid Nov 19 '13 at 12:25
18

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.

gx_
  • 4,690
  • 24
  • 31
  • 1
    Okay, so apparently the problem that prompted me to ask this question is highly specific and localised… – kccqzy Nov 16 '13 at 18:33