Closed cor3ntin closed 9 months ago
Similarly
An aggregate initialization is an immediate invocation if it evaluates a default member initializer that has a subexpression that is an immediate-escalating expression.
An immediate-escalating expression shall appear only in an immediate-escalating function.
Is the intent that aggregate initialization constitute an immediate-escalating context?
The more I try to think about it, the less it makes sense to me. Maybe we just want to say that
An aggregate initialization is an immediate-escalating expression if it evaluates a default member initializer that has a subexpression that is an immediate-escalating expression.
CWG2760
Thanks a lot, i think your complete proposed resolution makes sense to me.
... or not!
Here is what gives me pause:
An aggregate initialization is an immediate invocation if it evaluates a default member initializer that has a subexpression that is an immediate-escalating expression.
An immediate invocation ([expr.const]) that is a potentially-evaluated subexpression ([intro.execution]) of a default member initializer is neither evaluated nor checked for whether it is a constant expression at the point where the subexpression appears.
An immediate invocation ([expr.const]) that is not evaluated where it appears ([dcl.fct.default], [class.mem.general]) is evaluated and checked for whether it is a constant expression at the point where the enclosing initializer is used in a function call, a constructor definition, or an aggregate initialization.
So, given
consteval int f(int x) {
return x;
}
struct S {
int i;
int j = f(i);
};
S s(0);
To know whether the initialization of S
is an immediate invocation, I need to try to try to evaluate f
, which i can only do by...initializing S. There is a nasty circular dependency here and I don't how implementations may solve that except by trying to initialize twice, which, if possible, doesn't appear practical.
Note that it's very possible I'm missing something, but i think aggregates require a bit more thought or clarification. It's very different from, eg, a defaulted constructor where the default member initialization always happens in a non-immediate context that then escalates, before it is used. When initializing an aggregate we can't as easily decide the context we are currently in suddenly becomes immediate
One option would be to make the enclosing immediate-escalating function immediate, but that does not solve the above example.
To be clear, i think the resolution for this issue is fine but we probably want a separate issue to deal with aggregate initialization - it is a separate but related issue.
It is unclear whether default constructors of classes with immediate escalating default member initializers are immediate functions
In the above example
f()
is immediately escalating (f is an immediate function which is not a constant expression as it is not defined) [expr.const] /p16.2S::S()
is immediate-escalating (it is a defaulted special member function) [expr.const]/p17.2But is
S::S()
an immediate function?It is unclear that p18.2 applies (and p15 is irrelevant because S is not an aggregate)
It would be inconsistent for it not to be, so this is either an oversight or me missing something again!
Proposed resolution
Modify [expr.const] /p18
An immediate function is a function or constructor that is
or., or