| Document #: | P3795R1 [Latest] [Status] |
| Date: | 2026-01-09 |
| Project: | Programming Language C++ |
| Audience: |
CWG, LWG |
| Reply-to: |
Barry Revzin <barry.revzin@gmail.com> |
Since [P3795R0], fixing missing wording in
annotations_of and
data_member_spec (including
annotations), rebasing the wording. No longer pursuing the
is_inline,
is_constexpr,
is_consteval functions, those have
been removed from this paper.
At the Sofia meeting, [P2996R13] (Reflection for
C++26), [P3394R4] (Annotations for
Reflection), [P3293R3]
(Splicing a base class subobject), [P3491R3] (define_static_{string,object,array}),
[P3096R12]
(Function Parameter Reflection in Reflection for
C++26), and [P3560R2] (Error Handling in
Reflection) were all adopted. Because these were all
(somewhat) independent papers that were adopted at the same time, there
were a few inconsistencies that were introduced. Some gaps in coverage.
Some inconsistent APIs. This papers just seeks to correct a bunch of
those little issues.
This isn’t really one proposal, per se, as much as it is a number of very small proposals.
[P3795R0] proposed adding the predicates
is_inline(r),
is_constexpr(r),
and is_consteval(r).
After LEWG discussion in Kona, with questions about what is_constexpr(r)
actually means and whether it applies to implicitly
constexpr
functions as well as implicitly-declared ones, this proposal is simply
no longer pursuing those.
[P2996R13]’s mechanism for access
checking relies on std::meta::access_context.
However, that facility exposes another interesting bit of functionality
— albeit in a very indirect way. You can identify what class you’re
currently in:
consteval auto current_class(std::meta::info scope = std::meta::access_context::current().scope()) -> std::meta::info { while (true) { if (is_type(scope)) { return scope; } if (is_namespace(scope)) { throw std::meta::exception(/* ... */); } scope = parent_of(scope); } }
Given that this is a useful (and occasionally asked for) piece of information, we should just provide it directly, rather than having this API proliferate.
The same is true for
current_function and
current_namespace. The only open
question in my mind is whether there are situations in which you’d want
something like a
nearest_enclosing_class_or_namespace.
data_member_spec APIThe current API for std::meta::define_aggregate
requires calls to std::meta::data_member_spec,
which currently looks like this:
consteval info data_member_spec(info type, data_member_options options);
When we originally proposed this API, we allowed the name of
a non-static data member to be omitted. Doing so was akin to asking the
implementation to create a unique name for you. However, that changed in
[P2996R8] such that if
options.name
were not provided then the data member had to be an unnamed bit-field
(which meant that options.width had
to be provided). That means that while we originally envisioned it being
possible to implement tuple like
this:
consteval { define_aggregate( ^^storage, {data_member_spec(^^Ts)...} ); }
That’s no longer actually possible. You always have to provide
some member of options. So
tuple looks more like this (note
that you’re allowed to have multiple mambers named
_ now):
consteval { define_aggregate( ^^storage, {data_member_spec(^^Ts, {.name="_"})...} ); }
As such, it looks a little odd to have the type off by itself like that. It’s true that you always need to provide a type, but why is it special? Let’s face it, approximately nobody is going to be creating unnamed bit-fields, so the name is practically always present too.
Let’s just make the API more uniform:
struct data_member_options { struct name-type { // exposition only // ... }; + info type; optional<name-type> name; optional<int> alignment; optional<int> bit_width; bool no_unique_address = false; }; - consteval info data_member_spec(info type, data_member_options options); + consteval info data_member_spec(data_member_options options);
Additionally, while adding attributes is a difficult question, adding annotations is actually much simpler. With the adoption of [P3394R4], we should also allow you to add annotations to your generated members.
So the full change is really:
struct data_member_options { struct name-type { // exposition only // ... }; + info type; optional<name-type> name; optional<int> alignment; optional<int> bit_width; bool no_unique_address = false; + vector<info> annotations = {}; }; - consteval info data_member_spec(info type, data_member_options options); + consteval info data_member_spec(data_member_options options);
[P3394R4] (Annotations for Reflection) and [P3096R12] (Function Parameter Reflection in Reflection for C++26) were independent proposals adopted at the same time. The former gave us annotations, but the latter gave us the ability to introspect function parameters. Neither could really, directly add support for adding annotations onto function parameters. So, as a result, we don’t have them.
But there isn’t any particular reason why we shouldn’t support this:
auto f([[=1]] int x) -> void; constexpr auto a = annotations_of( parameters_of(^^f)[0] )[0]; static_assert([: constant_of(a) :] == 1);
[P1317R2] (Remove
return type deduction in
std::apply)
was also adopted in Sofia. It added three new type traits, but none of
us were aware of that paper, much less expected it to be adopted, so we
neglected to add consteval metafunction equivalents to those three type
traits:
consteval bool is_applicable_type(info fn, info tuple); consteval bool is_nothrow_applicable_type(info fn, info tuple); consteval info apply_result(info fn, info tuple);
This section in [P3795R0] pointed out how some functions
in std::meta::
still had a Constant When specification instead of a
Throws specification, but that was already resolved directly by
the handling of [P3560R2]. Nothing more is needed
here.
Currently, [P3560R2] specifies nothing about the contents of the exceptions being thrown on failure from various reflection functions. That’s largely as it should be — we really don’t need to specify the message, for instance.
But one thing that std::meta::exception
gives you is a
from()
accessor that tells you which operation failed. And that we
should specify — providing some front-matter sentence that says
that when a function or function template
F in <meta>
is specified to throw a meta::exception,
that from()
is ^^F.
Extend what an annotation can represent in 9.13.12 [dcl.attr.annotation]:
1 An annotation may be applied to any declaration of a type, type alias, variable, function, function parameter, namespace, enumerator,
base-specifier, or non-static data member.[ Note 1: * An annotation on a
parameter-declarationin a function definition applies to both the function parameter and the variable.[ Example 1:— end note ]— end example ]void f([[=1]] int x); void f([[=2]] int y) { constexpr info rp = parameters_of(^^f)[0]; constexpr info ry = variable_of(rp); static_assert(ry == ^^y); static_assert(annotations_of(rp).size() == 2); // both [1, 2] static_assert(annotations_of(ry).size() == 1); // just [2] }
Change 11.4.1 [class.mem.general] to extend our quintuple to a sextuple:
32 A data member description is a
quintuplesextuple (T,N,A,W,NUA,ANN) describing the potential declaration of a non-static data member where
- (32.1)
Tis a type,- (32.2)
Nis anidentifieror ⊥,- (32.3)
Ais an alignment or ⊥,- (32.4)
Wis a bit-field width or ⊥,and- (32.5)
NUAis a boolean value., and- (32.6)
ANNis a sequence of reflections representing either values or template parameter objects.Two data member descriptions are equal if each of their respective components are the same
entitiesentity,arethe same identifiers,have equal valuesthe same value, the same sequence, orareboth ⊥.
The synopsis change for 21.4.1 [meta.syn] is:
#include <initializer_list> namespace std::meta { using info = decltype(^^::); // ... // [meta.reflection.access.context], access control context struct access_context; // [meta.reflection.access.queries], member accessessibility queries consteval bool is_accessible(info r, access_context ctx); consteval bool has_inaccessible_nonstatic_data_members(info r, access_context ctx); consteval bool has_inaccessible_bases(info r, access_context ctx); consteval bool has_inaccessible_subobjects(info r, access_context ctx); + // [meta.reflection.scope], scope identification + consteval info current_function(); + consteval info current_class(); + consteval info current_namespace(); // [meta.reflection.member.queries], reflection member queries // ... // [meta.reflection.define.aggregate], class definition generation struct data_member_options; - consteval info data_member_spec(info type, data_member_options options); + consteval info data_member_spec(data_member_options options); consteval bool is_data_member_spec(info r); template<reflection_range R = initializer_list<info>> consteval info define_aggregate(info type_class, R&&); // associated with [meta.unary.cat], primary type categories // ... // associated with [meta.trans.other], other transformations consteval info remove_cvref(info type); consteval info decay(info type); template<reflection_range R = initializer_list<info>> consteval info common_type(R&& type_args); template<reflection_range R = initializer_list<info>> consteval info common_reference(R&& type_args); consteval info type_underlying_type(info type); template<reflection_range R = initializer_list<info>> consteval info invoke_result(info type, R&& type_args); consteval info unwrap_reference(info type); consteval info unwrap_ref_decay(info type); consteval size_t tuple_size(info type); consteval info tuple_element(size_t index, info type); + consteval bool is_applicable_type(info fn, info tuple); + consteval bool is_nothrow_applicable_type(info fn, info tuple); + consteval info apply_result(info fn, info tuple); consteval size_t variant_size(info type); consteval info variant_alternative(size_t index, info type); consteval strong_ordering type_order(info type_a, info type_b); // [meta.reflection.annotation], annotation reflection consteval vector<info> annotations_of(info item); consteval vector<info> annotations_of_with_type(info item, info type); }
Add to the front matter in 21.4.1 [meta.syn]:
1 Unless otherwise specified, each function, and each specialization of any function template, specified in this header is a designated addressable function ([namespace.std]).
* When a function or function template specialization
Fspecified in this header throws ameta::exceptionE,E.from()is a reflection representingFandE.where()is asource_locationrepresenting from where the call toForiginated.2 The behavior of any function specified in namespace
std::metais implementation-defined when a reflection of a construct not otherwise specified by this document is provided as an argument.
Adjust the data member description wording in 21.4.6 [meta.reflection.names]:
consteval bool has_identifier(info r);1 Returns […]
- (1.1) […]
- (1.13) Otherwise,
rrepresents a data member description (T,N,A,W,NUA,ANN) ([class.mem.general]);trueifNis not⊥. Otherwise,false.consteval string_view identifier_of(info r); consteval u8string_view u8identifier_of(info r);2 Let
Ebe […]3 Returns: An NTMBS, encoded with E, determined as follows:
Adjust the data member description wording in 21.4.7 [meta.reflection.queries]:
consteval info type_of(info r);2 Returns:
- (2.6) Otherwise, for a data member description (
T,N,A,W,NUA,ANN) ([class.mem.general]), a reflection of the typeT.consteval bool is_bit_field(info r);19 Returns:
trueifrrepresents a bit-field, or ifrrepresents a data member description (T,N,A,W,NUA,ANN) ([class.mem.general]) for whichWis not ⊥. Otherwise,false.
Add the new subclause [meta.reflection.scope] before 21.4.8 [meta.reflection.access.context].
[ Drafting note: The
wording for eval-point (p3)
and ctx-scope (p4) is moved
wholesale from access_context::current.
Paragraphs 1, 2, and 5 onwards are new ]:
1 The functions in this subclause retrieve information about where in the program they are invoked.
2 None of the functions in this subclause is an addressable function ([namespace.std]).
3 Given a program point
P, leteval-point(P)be the following program point:
- (3.1) If a potentially-evaluated subexpression ([intro.execution]) of a default member initializer
Ifor a member of a classC([class.mem.general]) appears atP, then a point determined as follows:
- (3.1.1) If an aggregate initialization is using
I,eval-point(Q), whereQis the point at which that aggregate initialization appears.- (3.1.2) Otherwise, if an initialization by an inherited constructor ([class.inhctor.init]) is using
I, a point whose immediate scope is the class scope corresponding toC.- (3.1.3) Otherwise, a point whose immediate scope is the function parameter scope corresponding to the constructor definition that is using
I.- (3.2) Otherwise, if a potentially-evaluated subexpression of a default argument ([dcl.fct.default]) appears at
P,eval-point(Q), whereQis the point at which the invocation of the function ([expr.call]) using that default argument appears.- (3.3) Otherwise, if the immediate scope of
Pis a function parameter scope introduced by a declarationD, andPappears either before the locus ofDor within the trailingrequires-clauseofD, a point whose immediate scope is the innermost scope enclosing the locus ofDthat is not a template parameter scope.- (3.4) Otherwise, if the immediate scope of
Pis a function parameter scope introduced by alambda-expressionLwhoselambda-introducerappears at pointQ, andPappears either within thetrailing-return-typeor the trailingrequires-clauseofL,eval-point(Q).- (3.5) Otherwise, if the innermost non-block scope enclosing
Pis the function parameter scope introduced by aconsteval-block-declaration([dcl.pre]), a point whose immediate scope is that inhabited by the outermostconsteval-block-declarationDcontainingPsuch that each scope (if any) that intervenes betweenPand the function parameter scope introduced byDis either- (3.6) Otherwise,
P.4 Given a scope
S, letctx-scope(S)be the following scope:
- (4.1) If
Sis a class scope or a namespace scope,S.- (4.2) Otherwise, if
Sis a function parameter scope introduced by the declaration of a function,S.- (4.3) Otherwise, if
Sis a lambda scope introduced by alambda-expressionL, the function parameter scope corresponding to the call operator of the closure type forL.- (4.4) Otherwise,
ctx-scope(S')whereS'is the parent scope ofS.5 Let
CURRENT-SCOPE(P)for a pointPbe a reflection representing the function, class, or namespace whose corresponding function parameter scope, class scope, or namespace scope, respectively, isctx-scope(S), whereSis the immediate scope ofeval-point(P).consteval info current_function();6 An invocation of
current_functionthat appears at a program pointPis value-dependent ([temp.dep.contexpr]) ifeval-point(P)is enclosed by a scope corresponding to a templated entity.7 Let
SbeCURRENT-SCOPE(P)wherePis the point at which the invocation ofcurrent_functionlexically appears.8 Throws:
meta::exceptionunlessSrepresents a function.9 Returns:
S.consteval info current_class();10 An invocation of
current_classthat appears at a program pointPis value-dependent ([temp.dep.contexpr]) ifeval-point(P)is enclosed by a scope corresponding to a templated entity.11 Let
SbeCURRENT-SCOPE(P)wherePis the point at which the invocation ofcurrent_classlexically appears.12 Throws:
meta::exceptionunlessSrepresents either a class or a member function.13 Returns:
SifSrepresents a class. Otherwise,parent_of(S).consteval info current_namespace();14 An invocation of
current_namespacethat appears at a program pointPis value-dependent ([temp.dep.contexpr]) ifeval-point(P)is enclosed by a scope corresponding to a templated entity.15 Let
SbeCURRENT-SCOPE(P)wherePis the point at which the invocation ofcurrent_namespacelexically appears.16 Returns:
SifSrepresents a namespace. Otherwise, a reflection representing the nearest enclosing namespace of the entity represented byS.
Adjust down the now-moved wording from 21.4.8 [meta.reflection.access.context]:
static consteval access_context current() noexcept;5
currentis not an addressable function ([namespace.std]).6 Given a program point
P, leteval-point(P)be the following program point:
- (6.1) If a potentially-evaluated subexpression ([intro.execution]) of a default member initializer
Ifor a member of a classC([class.mem.general]) appears atP, then a point determined as follows:
- (6.1.1) If an aggregate initialization is using
I,eval-point(Q), whereQis the point at which that aggregate initialization appears.- (6.1.2) Otherwise, if an initialization by an inherited constructor ([class.inhctor.init]) is using
I, a point whose immediate scope is the class scope corresponding toC.- (6.1.3) Otherwise, a point whose immediate scope is the function parameter scope corresponding to the constructor definition that is using
I.- (6.2) Otherwise, if a potentially-evaluated subexpression of a default argument ([dcl.fct.default]) appears at
P,eval-point(Q), whereQis the point at which the invocation of the function ([expr.call]) using that default argument appears.- (6.3) Otherwise, if the immediate scope of
Pis a function parameter scope introduced by a declarationD, andPappears either before the locus ofDor within the trailingrequires-clauseofD, a point whose immediate scope is the innermost scope enclosing the locus ofDthat is not a template parameter scope.- (6.4) Otherwise, if the immediate scope of
Pis a function parameter scope introduced by alambda-expressionLwhoselambda-introducerappears at pointQ, andPappears either within thetrailing-return-typeor the trailingrequires-clauseofL,eval-point(Q).- (6.5) Otherwise, if the innermost non-block scope enclosing
Pis the function parameter scope introduced by aconsteval-block-declaration([dcl.pre]), a point whose immediate scope is that inhabited by the outermostconsteval-block-declarationDcontainingPsuch that each scope (if any) that intervenes betweenPand the function parameter scope introduced byDis either- (6.6) Otherwise,
P.7 Given a scope
S, letctx-scope(S)be the following scope:
- (7.1) If
Sis a class scope or a namespace scope,S.- (7.2) Otherwise, if
Sis a function parameter scope introduced by the declaration of a function,S.- (7.3) Otherwise, if
Sis a lambda scope introduced by alambda-expressionL, the function parameter scope corresponding to the call operator of the closure type forL.- (7.4) Otherwise,
ctx-scope(S')whereS'is the parent scope ofS.8 An invocation of
currentthat appears at a program pointPis value-dependent ([temp.dep.contexpr]) ifeval-point(P)is enclosed by a scope corresponding to a templated entity.9 Returns: An
access_contextwhose designating class is the null reflection and whose scoperepresents the function, class, or namespace whose corresponding function parameter scope, class scope, or namespace scope isisctx-scope(S), whereSis the immediate scope ofeval-point(P)andCURRENT-SCOPE(P)wherePis the point at which the invocation ofcurrentlexically appears.
Adjust the data member description wording in 21.4.11 [meta.reflection.layout]:
consteval size_t size_of(info r);5 Returns:
- (5.1) If
rrepresents a non-static data member of typeTor a a data member description (T,N,A,W,NUA,ANN), or6 Throws:
meta::exceptionunless all of the following conditions are met:
- (6.1)
dealias(r)is a reflection of a type, object, value, variable of non-reference type, non-static data member that is not a bit-field, direct base class relationship, or data member description (T,N,A,W,NUA,ANN) ([class.mem.general]) whereWis ⊥.consteval size_t alignment_of(info r);7 Returns:
- (7.5) Otherwise,
rrepresents a data member description (T,N,A,W,NUA,ANN) ([class.mem.general]). IfAis not ⊥, then the valueA. Otherwise,alignment_of(^^T).8 Throws:
meta::exceptionunless all of the following conditions are met:
- (8.1)
dealias(r)is a reflection of a type, object, variable of non-reference type, non-static data member that is not a bit-field, direct base class relationship, or data member description (T,N,A,W,NUA,ANN) ([class.mem.general]) whereWis ⊥.consteval size_t bit_size_of(info r);9 Returns:
- (9.2) Otherwise, if r represents a data member description (
T,N,A,W,NUA,ANN) andWis not ⊥, thenW.
Change 21.4.12 [meta.reflection.annotation]:
[ Drafting note: This
doesn’t work for data member specifications, because those contain
constants — not annotations yet. Is it even useful to pull them back
out? They’re not annotations yet. Do we have to synthesize annotations?
It’s useful to add annotations to generated data members — I’m not sure
if it’s useful to query the annotations on a pre-generated data member?
There’s also a drive-by cleanup of the wording around direct base class
relationships, since
base-specifiers aren’t
technically declared. ]
consteval vector<info> annotations_of(info item);1 For a function
F, letS(F)be the set of declarations, ignoring any explicit instantiations, that declare eitherFor a templated function of whichFis a specialization.1 Let
Ebe2 Returns: a
vectorcontaining all of the reflectionsRrepresenting each annotation applying toeach declaration of:Ethat
- (2.1) if
itemrepresents a function parameterPof a functionF, then the declaration ofPin each declaration ofFinS(F),- (2.2) otherwise, if
itemrepresents a functionF, then each declaration ofFinS(F),- (2.3) otherwise, if
itemrepresents a direct base class relationship (D,B), then the correspondingbase-specifierin the definition ofD,- (2.4) otherwise, each declaration of the entity represented by
item,such that each specified declaration precedes either some point in the evaluation context ([expr.const]) or a point immediately following the
class-specifierof the outermost class for which such a point is in a complete-class context. For any two reflectionsR1andR2in the returnedvector, if the annotation represented byR1precedes the annotation represented byR2, thenR1appears beforeR2. IfR1andR2represent annotations from the same translation unitT, any element in the returnedvectorbetweenR1andR2represents an annotation fromT.[ Note 2: The order in which two annotations appear is otherwise unspecified. — end note ]
3 Throws:
meta::exceptionunless item represents a type, type alias, variable, function, function parameter, namespace, enumerator, direct base class relationship, or non-static data member.
Change the data_member_spec API
in 21.4.16 [meta.reflection.define.aggregate]
[ Drafting note: The
nested example in the note is now separate from the note ]:
struct data_member_options { struct name-type { // exposition only template<class T> requires constructible_from<u8string, T> consteval name-type(T &&); template<class T> requires constructible_from<string, T> consteval name-type(T &&); private: variant<u8string, string> contents; // exposition only }; + info type; optional<name-type> name; optional<int> alignment; optional<int> bit_width; bool no_unique_address = false; + vector<info> annotations; };1 The classes
data_member_optionsanddata_member_options::name-typeare consteval-only types ([basic.types.general]), and are not structural types ([temp.param]).template <class T> requires constructible_from<u8string, T> consteval name-type(T&& value);2 Effects: Initializes
contentswithu8string(std::forward<T>(value)).template<class T> requires constructible_from<string, T> consteval name-type(T&& value);3 Effects: Initializes
contentswithstring(std::forward<T>(value)).[ Note 3: The class
name-typeallows the functiondata_member_specto accept an ordinary string literal (orstring_view,string, etc.) or a UTF-8 string literal (oru8string_view,u8string, etc.) equally well. — end note ][ Example 2:— end example ]consteval void fn() { - data_member_options o1 = {.name="ordinary_literal_encoding"}; - data_member_options o2 = {.name=u8"utf8_encoding"}; + data_member_options o1 = {.type=^^int, .name="ordinary_literal_encoding"}; + data_member_options o2 = {.type=^^char, .name=u8"utf8_encoding"}; }- consteval info data_member_spec(info type, - data_member_options options); + consteval info data_member_spec(data_member_options options);4 Returns: A reflection of a data member description (
T,N,A,W,NUA,ANN) ([class.mem.general]) where
- (4.1)
Tis the type represented bydealias(options.type),- (4.2)
Nis either the identifier encoded byoptions.nameor ⊥ ifoptions.namedoes not contain a value,- (4.3)
Ais either the alignment value held byoptions.alignmentor ⊥ ifoptions.alignmentdoes not contain a value,- (4.4)
Wis either the value held byoptions.bit_widthor ⊥ ifoptions.bit_widthdoes not contain a value,and- (4.5)
NUAis the value held byoptions.no_unique_address., and- (4.6)
ANNis the sequence of valuesconstant_of(r)for eachrinoptions.annotations.[ Note 4: The returned reflection value is primarily useful in conjunction with
define_aggregate; it can also be queried by certain other functions instd::meta(e.g.,type_of,identifier_of). — end note ]5 Throws:
meta::exceptionunless the following conditions are met:
- (5.1)
dealias(options.type)represents either an object type or a reference type;- (5.2) if
options.namecontains a value, then:[ Note 5: The name corresponds to the spelling of an identifier token after phase 6 of translation ([lex.phases]). Lexical constructs like
- (5.2.1)
holds_alternative<u8string>(options.name->contents)istrueandget<u8string>(options.name->contents)contains a valid identifier ([lex.name]) that is not a keyword ([lex.key]) when interpreted with UTF-8, or- (5.2.2)
holds_alternative<string>(options.name->contents)istrueandget<string>(options.name->contents)contains a valid identifier that is not a keyword when interpreted with the ordinary literal encoding;universal-character-names [lex.universal.char] are not processed and will cause evaluation to fail. For example,R"(\u03B1)"is an invalid identifier and is not interpreted as"α". — end note ]- (5.3) if
options.namedoes not contain a value, thenoptions.bit_widthcontains a value;- (5.4) if
options.bit_widthcontains a valueV, then- (5.5) if
options.alignmentcontains a value, it is an alignment value ([basic.align]) not less thanalignment_of(options.type).; and- (5.6) for every reflection
rinoptions.annotations,type_of(r)represents a non-array object type, and evaluation ofconstant_of(r)does not exit via an exception.template<reflection_range R = initializer_list<info>> consteval info define_aggregate(info class_type, R&& mdescrs);7 Let
Cbe the class represented byclass_typeandrKbe theKth reflection value inmdescrs. For everyrKinmdescrs, let (TK,NK,AK,WK,NUAK,ANNK) be the corresponding data member description represented byrK.8 Constant When: […]
9 Produces an injected declaration
D([expr.const]) that definesCand has properties as follows:
(9.1) The target scope of
Dis […](9.2) The locus of
D[…](9.3) The characteristic sequence of
D[…](9.4) If
Cis a specialization […](9.5) For each
rK, there is a corresponding entityMKbelonging to the class scope ofDwith the following properties:
- (9.5.1) If
NKis ⊥,MKis an unnamed bit-field. Otherwise,MKis a non-static data member whose name is the identifier determined by the character sequence encoded byNKin UTF-8.- (9.5.2) The type of
MKisTK.- (9.5.3)
MKis declared with the attribute[[no_unique_address]]if and only ifNUAKistrue.- (9.5.4) If
WKis not ⊥,MKis a bit-field whose width is that value. Otherwise,MKis not a bit-field.- (9.5.5) If
AKis not ⊥,MKhas thealignment-specifieralignas(AK). Otherwise,MKhas noalignment-specifier.- (9.5.6)
MKhas an annotation whose underlying constant ([dcl.attr.annotation]) isrfor every reflectionrinANNK.(9.6) For every
rLinmdescrssuch thatK < L[…]
std::apply.
define_static_{string,object,array}.