5

Here is my code:

class agg_t1{
    int x;      // private non-static data menber
};
class agg_t2{
    agg_t2(){}      // user-provided constructor
};
constexpr void ce1(agg_t1 arg){};       // OK
constexpr void ce2(agg_t2 arg){};       // ERROR:  parameter type 'agg_t2' is not a literal type 

According to dcl.constexpr:

The definition of a constexpr function shall satisfy the following requirements: ...

  • each of its parameter types shall be a literal type; ...

And basic#types.general-10:

A type is a literal type if it is: ...

  • it is either a closure type, an aggregate type, or ...

I understand the reason why agg_t2 is not a literal type is that, it violates the rule dcl.init.aggr#1.1:

An aggregate is an array or a class with ...

  • no user-declared or inherited constructors ...

and I think agg_t1 may not be a literal type because it violates the rule dcl.init.aggr#1.1 too:

An aggregate is an array or a class with ...

  • no private or protected direct non-static data members ...

However... the compiler result tells me I was wrong about the assumption for agg_t1.

My question is:

If agg_t1's private data member x makes it non-aggregate type ,then why the agg_t1 type is permitted in constexpr function definition of ce1?

10
  • 2
    There were words that come after "aggregate type" in the definition of what types are literal types. Those words might be important. Commented Dec 28, 2021 at 4:59
  • 1
    Your argument is that neither agg_t1 nor agg_t2 are aggregates, despite their cleverly-chosen names. You have yet to eliminate the other possibilities by which they might be literal types (closure type and whatever you cut off after the "or" -- as with the "or" operator, proving that one operand to || is false does not make the entire expression false.) Commented Dec 28, 2021 at 5:11
  • 2
    "... or has at least one constexpr constructor or constructor template (possibly inherited ...) that is not a copy or move constructor," Commented Dec 28, 2021 at 5:47
  • 2
    That part after or: "... or has at least one constexpr constructor or constructor template". It so happens that an implicitly-declared default constructor for agg_t1 is constexpr, per [class.default.ctor]/4 Commented Dec 28, 2021 at 5:51
  • 1
    Add -std=c++20 switch, then your test compiles. Commented Dec 28, 2021 at 13:37

1 Answer 1

0

If agg_t1's private data member x makes it non-aggregate type, then why the agg_t1 type is permitted in constexpr function definition of ce1?


C++20

agg_t1 is indeed not an aggregate class due to its private data member, however whilst aggregateness can be one of the sufficient requirements for class type to be a literal type, it is not a necessary one. Note the or condition of [basic.types.general]/10.5.2 [emphasis mine]:

A type is a literal type if it is:

  • [...]
  • a possibly cv-qualified class type that has all of the following properties:
    • [...]
    • it is either a closure type ([expr.prim.lambda.closure]), an aggregate type ([dcl.init.aggr]), or has at least one constexpr constructor or constructor template (possibly inherited ([namespace.udecl]) from a base class) that is not a copy or move constructor, [...]

As per [class.default.ctor]4:

[...] If that user-written default constructor would satisfy the requirements of a constexpr constructor ([dcl.constexpr]), the implicitly-defined default constructor is constexpr [...]

and [dcl.constexpr]/3 and [dcl.constexpr]/4:

/3 The definition of a constexpr function shall satisfy the following requirements:

  • [...]
  • if the function is a constructor or destructor, its class shall not have any virtual base classes; [...]

/4 The definition of a constexpr constructor whose function-body is not = delete shall additionally satisfy the following requirements:

  • for a non-delegating constructor, every constructor selected to initialize non-static data members and base class subobjects shall be a constexpr constructor;
  • [...]

the implicitly-defined default constructor of agg_t1 is constexpr, and thus [basic.types.general]/10.5.2 does not disqualify agg_t1 from being a literal type in C++20.


C++17

In C++17 the implicitly-defined default constructor of agg_t1 is not constexpr, as it violates [dcl.constexpr]/4.5:

The definition of a constexpr constructor shall satisfy the following constraints:

  • [...]

In addition, either its function-body shall be = delete, or it shall satisfy the following constraints:

  • [...]
  • /4.5 every non-variant non-static data member and base class sub-object shall be initialized ([class.base.init]);

And indeed, whilst the following is rejected by both Clang and GCC for -std=c++17:

class A {
    constexpr A() = default;  // error: cannot be constexpr
private:
    int x;
};

the following is accepted:

// OK: B is a literal type.
class B {
    constexpr B() = default;  // OK
private:
    int x{};
};
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the patient explanations. While being convinced by most of it, I doubt this: "In C++17... agg_t1 is not constexpr...it violates...". Here(11,14,17,20) shows that ce1 accepts the type agg_t1, which has only one private data member, meaning that agg_t1 must be a valid LiteralType. Since we all agree that agg_t1 is not an "aggregate", according to the requirements(basic.types.general#10.5) of LiteralType, then if its implicitly-defined constructor is still not "constexpr", what else it could be that makes it become a valid LiteralType?
@absuu Afaict this seems like a standard defect. And indeed, open issue CWG 1360 writes: "According to 11.4.5 [class.ctor] paragraph 6, a defaulted default constructor is constexpr if the corresponding user-written constructor would satisfy the constexpr requirements. However, the requirements apply to the definition of a constructor, and a defaulted constructor is defined only if it is odr-used, leaving it indeterminate at declaration time whether the defaulted constructor is constexpr or not.".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.