Skip to content

CWG2852 [class.mem.general] Is a default argument of a class-scope lambda a complete-class context? #449

Description

@zygoloid

Full name of submitter (unless configured in github; will be published with the issue): Richard Smith

Reference (section label): [class.mem.general]/7

Link to reflector thread (if any): None, but GCC bugzilla thread: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111923

Issue description:

[class.mem.general]/7 is overly broad:

A complete-class context of a class (template) is a
(7.1) function body ([dcl.fct.def.general]),
(7.2) default argument ([dcl.fct.default]),
(7.3) default template argument ([temp.param]),
(7.4) noexcept-specifier ([except.spec]), or
(7.5) default member initializer
within the member-specification of the class or class template.

The "within" here appears to cover such things appearing indirectly within the member-specification, such as a default argument or default template argument of a class-scope lambda-expression:

struct X {
  static constexpr auto a = [] (int n = X::n) {};
  static constexpr int n = 5;
};

Similarly, the current rule appears to treat noexcept-specifiers and default arguments appearing as part of all function types in the class body as complete-class context, rather than only those introduced in the declaration of a member or friend function:

struct Y {
  // *noexcept-specifier* is a complete-class context,
  // but presumably was not intended to be.
  void (*p)(int n) noexcept(sizeof(Y) == 1);
};

Suggested resolution:

Change in [class.mem.general]/7:

A complete-class context of a class (template) or class template C is a
— function body ([dcl.fct.def.general]),
— default argument ([dcl.fct.default]) of a function declaration,
— default template argument ([temp.param]),
— noexcept-specifier ([except.spec]) of a function declaration, or
— default member initializer, or
— complete-class context of a nested class defined in C, recursively
where the function, template, non-static data member, or nested class is declared by a member-declaration of C. within the member-specification of the class or class template.
[Note 4: A complete-class context of a nested class is also a complete-class context of any enclosing class, if the nested class is defined within the member-specification of the enclosing class. — end note]

Activity

  1. stsp commented on Oct 25, 2023

    @stsp

    The aforementioned bugzilla thread
    was intended to point out that some
    standard-compliant code is currently
    rejected by gcc. Namely, this code was
    supposed to work:

    template <auto O>
    struct B {
        static constexpr int off = O();
    };
    
    struct A {
        char a;
        B<[]() static constexpr ->int { return offsetof(A, b); }> b;
    };
    

    due to [class.mem.general]/7.1 (function body) + note4 for closure sub-class.

    The suggested resolution should be to
    explicitly list "closure type" in "Note 4",
    rather than to rephrase and exclude
    the "closure type" from a "complete class
    context" candidates.

  2. jensmaurer commented on Feb 3, 2024

    @jensmaurer
    Member

    @stsp: No, not as a core issue. Please write a paper targeted at EWG if you wish to extend the complete-class contexts; there are non-trivial implementation concerns.

    CWG2852

  3. changed the title [-][class.mem.general] Is a default argument of a class-scope lambda a complete-class context?[/-] [+]CWG2852 [class.mem.general] Is a default argument of a class-scope lambda a complete-class context?[/+] on Feb 3, 2024
  4. stsp commented on Feb 4, 2024

    @stsp

    @jensmaurer Well, all I need is to use
    offsetof() in templates. If you tell me how
    to do that, I won't need to write any
    EWG paper. But so far, every once I find
    a way to do that, it immediately gets
    disallowed. First one was reinterpret_cast
    in constexpr, which could trivially be used
    to calc an offset - disallowed, even if it
    actually worked and my code was using it.
    Lambdas in class-complete context - disallowed.
    Come one, just tell me how to use offsetof() then...
    Don't only disallow, give me a work-around!

  5. jensmaurer commented on Feb 4, 2024

    @jensmaurer
    Member

    EWG is the right group to discuss extensions to the language, not this forum. Side note: CWG2784 discusses an issue with interpreting the term "member-designator" from C's specification of "offsetof" in a C++ context.

  6. zygoloid commented on Feb 4, 2024

    @zygoloid
    MemberAuthor

    @stsp Instead of adding another complete-class context, you can make the class type dependent and defer performing instantiation until the class is complete: https://godbolt.org/z/sjbKGnqva

    (This is off-topic for this forum, but hopefully this example can save you and EWG some time.)

  7. stsp commented on Feb 5, 2024

    @stsp

    That works, thank you!
    Just a few minor downsides:

    • c++-20-specific
    • such lambda seems to be no longer convertible to function pointer, as normal lambdas are

    But its a working solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions