This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115c. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-09-25


1836. Use of class type being defined in trailing-return-type

Section: _N4567_.5.1.1  [expr.prim.general]     Status: CD5     Submitter: Mike Miller     Date: 2014-01-17

[Voted into the WP at the July, 2017 meeting.]

According to _N4567_.5.1.1 [expr.prim.general] paragraph 3,

Unlike the object expression in other contexts, *this is not required to be of complete type for purposes of class member access (7.6.1.5 [expr.ref]) outside the member function body.

Is this special treatment of member access expressions intended to apply only to *this, or does it apply to other ways of specifying the class being defined in the object expression? For example,

  struct S {
    int i;
    auto f1() -> decltype((*this).i);      // okay
    auto f2(S& This) -> decltype(This.i);  // okay?
    auto f3() -> decltype(((S*)0)->i);     // okay?
  };

There is implementation divergence on this question.

If the intent is to allow object expressions other than *this to have the current class type, this specification should be moved from _N4567_.5.1.1 [expr.prim.general] to 7.6.1.5 [expr.ref] paragraph 2, which is where the general requirement for complete object expression types is found.

On a related point, the note immediately following the above-cited passage is not quite correct:

[Note: only class members declared prior to the declaration are visible. —end note]

This does not apply when the member is a “member of an unknown specialization,” per 13.8.3.2 [temp.dep.type] bullet 5.3 sub-bullet 1; for example,

  template<typename T> struct S : T {
    auto f() -> decltype(this->x);
  };

Here x is presumed to be a member of the dependent base T and is not “declared prior to the declaration” that refers to it.

Proposed resolution (May, 2017):

  1. Change 7.5.3 [expr.prim.this] paragraph 2 as follows:

  2. Unlike the object expression in other contexts, *this is not required to be of complete type for purposes of class member access (7.6.1.5 [expr.ref]) outside the member function body. [Note: Only class members declared prior to the declaration are visible. —end note] [Note: In a trailing-return-type, the class being defined is not required to be complete for purposes of class member access (7.6.1.5 [expr.ref]). Class members declared later are not visible. [Example:

      struct A {
        char g();
        template<class T> auto f(T t) -> decltype(t + g())
        { return t + g(); }
      };
      template auto A::f(int t) -> decltype(t + g());
    

    end example] end note]

  3. Change 7.6.1.5 [expr.ref] paragraph 2 as follows, splitting the paragraph into two paragraphs as indicated:

  4. For the first option (dot) the first expression shall be a glvalue having complete class type. For the second option (arrow) the first expression shall be a prvalue having pointer to complete class type. In both cases, the class type shall be complete unless the class member access appears in the definition of that class. [Note: If the class is incomplete, lookup in the complete class type is required to refer to the same declaration (6.4.7 [basic.scope.class]). —end note]

    The expression E1->E2 is converted to the equivalent form (*(E1)).E2; the remainder...