---
title: "Attributes reflection"
document: P3385R8
date: 2026-06-08
audience: EWG, LEWG
reply-to:
  - "Aurelien Cassagnes <acassagnes@bloomberg.net>"
---

## Revision history

Since [P3385R6]:

- Remove support for `[[assume]]`
- Rebase wording

Since [P3385R5]:

- Merge [P3678R0] into current paper following SG7 recommendation
- Address feedback from Sofia
- Remove `appertain`
- Add `has_attribute` metafunction

Since [P3385R4]:

- Remove in-place splicer syntax
- Add `appertain` metafunction
- Augment implementation feedback

Since [P3385R3]:

- Rework wording around lookup of splice-expression
- Update authors
- Rebase on [P2996R9]

Since [P3385R2]:

- Address scoped attributes
- Address attribute argument clause

Since [P3385R1]:

- Fix various issues with samples
- Fix wording
- Rebase on [P2996R11]

Since [P3385R0]:

- Reword ignorability section to mention set of rules
- Discuss argument clause in `info` equal operator
- Discuss appertaining to null statement in reflect expression

## Introduction

Attributes are used to a great extent, and there is new attributes being added to the language somewhat regularly. As reflection makes its way into our standard, we are missing a way for generic code to look into the attributes appertaining to an entity. That is what this proposal aims to tackle by introducing the building blocks.

### Motivation

A core motivation shows up when looking at `define_aggregate` design

```
namespace std::meta {
  struct data_member_options {
    struct name_type {
      template <typename T> requires constructible_from<u8string, T>
        consteval name_type(T &&);

      template <typename T> requires constructible_from<string, T>
        consteval name_type(T &&);
    };

    optional<name_type> name;
    optional<int> alignment;
    optional<int> bit_width;
    bool no_unique_address = false;
    vector<info> annotations;
  };
}
```

Here we have 2 attributes showing up in `alignas` and `[[no_unique_adress]]`. We have no way to appertain `[[deprecated]]`, `[[maybe_unused]]`, or any other attributes. Additionally, if one wants to explicitly tell their compiler to enforce this attribute, they may want to tag `[[msvc::no_unique_address]]` out of caution. Synthesizing enumerators in [P4033R0] also leverage a generic way to carry attributes for the feature to be complete... Having a uniform vehicle to solve this design concern is a major motivation for this paper, committee time should not be spent addressing "Should we add a field to data_member_options ?" everytime we think about standardizing an attribute.

We also expect a number of applications for attribute introspection to happen in the context of code generation [P2237R0], where for example, one may want to skip over `[[deprecated]]` members, explicitly tag python bindings with `@deprecated` decorators, etc.

The following example demonstrates cloning an aggregate while leaving out any deprecated members:

```
constexpr auto ctx = std::meta::access_context::current();

struct User {
  [[deprecated]] std::string name;
  [[deprecated]] std::string country;
  std::string uuidv5;
  std::string countryIsoCode;
};

template<class T>
struct MigratedT {
  struct impl;
  consteval {
    std::vector<std::meta::info> migratedMembers = {};
    for (auto member : nonstatic_data_members_of(^^T, ctx)) {
      if (!std::meta::has_attribute(member, ^^[[deprecated]])) {
        migratedMembers.push_back(data_member_spec(
          std::meta::type_of(member),
          {.name = std::meta::identifier_of(member)}
        ));
      }
    }
    define_aggregate(^^impl, migratedMembers);
  }
};

using MigratedUser =  MigratedT<User>::impl;
static_assert(std::meta::nonstatic_data_members_of(^^User, ctx).size() == 4);
static_assert(std::meta::nonstatic_data_members_of(^^MigratedUser, ctx).size() == 2);

int main() {
  MigratedUser newUser;
  // Uncomment the following line to show the deprecated fields are gone
  //   newUser.name = "bob";
  //
  // error: no member named 'name' in 'MigratedT<User>::impl'
  // 142 |   newUser.name = "bob";
  //
  newUser.uuidv5 = "bob";
}
```

[link](https://godbolt.org/z/35Y3Tf773).

## Scope

Before longer discussions, we can give a **simplified** view of what the initial support is expected to be

<!-- tomd:lossy-table -->
| Category | Sample | Rationale |
| --- | --- | --- |
| 🟢 No argument | [[ nodiscard ]] |  |
| 🟢 Trivial argument | [[ gnu :: constructor ( 100 )]] |  |
| 🟡 Complex argument | [[ clang :: availability ( macos , introduced = 10.4 )]] | Custom parsing rules for arguments |
| 🔴 Unknown | [[ my :: attribute ( freeform arg )]] | Undefined parsing |

*Complex* here refers handwaving-ly to our implementation experience dealing with positional arguments. Experienced implementers may feel differently, what remains true is that we want to leave the choice to implementers to opt out for problematic attributes.

### Argument clause

Let us recap here what are the attributes 9.13 [dcl.attr] found in the standard and their argument clause

<!-- tomd:lossy-table -->
| Attribute | Argument-clause |
| --- | --- |
| assume | conditional-expression |
| deprecated | unevaluated-string |
| fallthrough | N/A |
| indeterminate | N/A |
| likely | N/A |
| maybe_unused | N/A |
| nodiscard | unevaluated-string |
| noreturn | N/A |
| no_unique_address | N/A |
| unlikely | N/A |

Feedback post Wroclaw was unanimous on treating the argument clause as a salient property (and so starting from the second revision `^^[[nodiscard("foo")]] != ^^[[nodiscard("bar")]]`).

While it brings no concern for attributes like `nodiscard`, it is more an open question [1] when it comes to an attribute like `assume` accepting an expression as argument… Should `^^[[assume(i + 1)]]` compare equal to `^^[[assume(1 + i)]])`?

Ultimately to not force a particular strategy until progress is made on reflection of expressions, the current proposal does not allow `^^[[assume(expr)]]`.

In our experimental implementation, we show a possible strategy where we do not transform the expression into a canonical representation before evaluating equality via profiling and so `^^[[assume(i + 1)]]` is not the same as `^^[[assume(1 + i)]])`.

### Support, optionality and self consistency

Here we introduce an alternative terminology to guide the conversation in the context of reflection and attributes

- Unknown attributes are **unsupported** (e.g, `[[my::attribute]]`).
- Standard attributes with `conditional-expression` argument are **unsupported** (e.g, `[[assume]]`).
- Every other standard attributes are **supported** (e.g., `[[nodiscard]]`)
- Vendor specific attributes are conditionally (up to an implementation) **supported** (e.g., `^^[[gnu::constructor(100)]]` is supported, `^^[[clang::availability(macos,introduced=10.4,deprecated=10.6,obsoleted=10.7)]]` is not)

With this category in place, we make the following design choice

1. Creating reflection of unsupported attribute is ill-formed (diagnostic required)
2. `attributes_of` does not return unsupported attributes

This is done to allow implementers the room to grow the set of supported attributes w/o worrying about breaking code. The current practice around creating reflection of problematic constructs (such as `using-declarator`) is to be ill-formed and so we’ll follow that here.

Diagnostic in those cicrumstances are not hard to emit, and so we should do so.

## Proposed Features

### Reflection expression

Our proposal advocates to support reflect expression like

```
constexpr auto r = ^^[[nodiscard("keepMe")]];
```

The result is a reflection value embedding salient property of the attribute which are the attribute namespace, token and the argument clause if any.

### Metafunctions

We propose to add a couple of metafunctions to what is available in `<meta>`. In addition, we will extend support to attributes in the other metafunctions when it makes sense.

#### attributes_of

```
namespace std::meta {
    consteval auto attributes_of(info construct) -> vector<info>;
}
```

`attributes_of()` returns a vector of reflections representing all individual attributes that appertain to `construct`.

Simple example follows

```
enum class [[nodiscard("Error discarded")]] ErrorCode {
  Disconnected,
  ConfigurationIncorrect,
  OutdatedCredentials,
};

static_assert(attributes_of(^^ErrorCode)[0] == ^^[[nodiscard("Error discarded")]]);
```

In the case where an entity is legally redeclared with different attribute arguments, `attribute_of` return one of those.

```
enum class ErrorCode;
enum class [[nodiscard("Error discarded")]] ErrorCode;
enum class [[nodiscard]] ErrorCode {
  Disconnected,
  ConfigurationIncorrect,
  OutdatedCredentials,
};

// Either of [[nodiscard("Error discarded")]] or [[nodiscard]]
static_assert(attributes_of(^^ErrorCode).size() == 1);
```

#### has_attribute

```
namespace std::meta {
  enum class attribute_comparison {
    ignore_namespace, // Namespace is ignored during the comparison
    ignore_argument,  // Arguments are ignored during the comparison
  };

  consteval auto has_attribute(info                 construct,
                               info                 attribute) -> bool;

  consteval auto has_attribute(info                 construct,
                               info                 attribute,
                               attribute_comparison policy) -> bool;
}
```

`has_attribute()` returns true if the specified `attribute` is found appertaining to `construct`, false otherwise.

Simple example follows

```
struct [[clang::consumable(unconsumed)]] F {
    [[clang::callable_when(unconsumed)]] void f() {}
};

static_assert(std::meta::has_attribute(^^F::f, ^^[[clang::callable_when(unconsumed)]]));
```

[link](https://godbolt.org/z/P1jW9o5qY)

The overload with `policy` parameter allows a combo of flags to dictate what part of an attribute are meaningful to the comparison. This comes in handy when we want to find out an attribute, ignoring the vendor prefix and or the particular message that is being attached here and there.

```
[[gnu::deprecated("Standard deprecated")]] void f() { }

// Ignore both the namespace and the argument
static_assert(std::meta::has_attribute(
  ^^f,
  ^^[[deprecated]],
  std::meta::attribute_comparison::ignore_namespace
    | std::meta::attribute_comparison::ignore_argument
));
```

[link](https://godbolt.org/z/jc1G9Y4rE)

#### is_attribute

```
namespace std::meta {
  consteval auto is_attribute(info r) -> bool;
}
```

`is_attribute()` returns true if `r` represents an attribute, it returns false otherwise. Its use is trivial

```
static_assert(is_attribute(^^[[nodiscard]]));
```

#### identifier_of, display_string_of

Given a reflection `r` designating an attribute, `identifier_of(r)` (resp. `u8identifier_of(r)`) should return a `string_view` (resp. `u8string_view`) corresponding to the `attribute-token`.

A sample follows

```
static_assert(identifier_of(^^[[clang::warn_unused_result("message")]])
  == "clang::warn_unused_result");
static_assert(identifier_of(^^[[nodiscard("message")]])
  == "nodiscard");
```

Given a reflection `r` that designates an individual attribute, `display_string_of(r)` (resp. `u8display_string_of(r)`) returns an unspecified non-empty `string_view` (resp. `u8string_view`). Implementations are encouraged to produce text that is helpful in identifying the reflected attribute for display purpose. In the preceding example we could imagine printing `[[clang::warn_unused_result("message")]]` as it might be better fitted for diagnostics.

#### data_member_spec, define_aggregate

To support arbitrary attributes appertaining to data members, we’ll need to augment `data_member_options` to encode attributes we want to attach here.

The structure changes thusly:

```
    namespace std::meta {
      struct data_member_options {
        struct name_type {
          template <typename T> requires constructible_from<u8string, T>
            consteval name_type(T &&);

          template <typename T> requires constructible_from<string, T>
            consteval name_type(T &&);
        };

        optional<name_type> name;
        optional<int> alignment;
        optional<int> bit_width;
        bool no_unique_address = false;
        [[deprecated]] bool no_unique_address = false;
        vector<info> attributes;
      };
    }
```

From there building an aggregate piecewise proceeds as usual

```
struct Empty{};
struct [[nodiscard]] S;
consteval {
  define_aggregate(^^S, {
    data_member_spec(^^int, {.name = "i"}),
    data_member_spec(^^Empty, {.name = "e",
                              .attributes = {^^[[msvc::no_unique_address]]}})
  });
}

// Equivalent to
// struct [[nodiscard]] S {
//   int i;
//   [[msvc::no_unique_address]] struct Empty { } e;
// };
```

Passing attributes through the above proposed approach is well in line with the philosophy of leveraging `info` as the opaque vehicle to carry every and all reflections.

## Proposed wording

### Language

#### [basic.fundamental] Fundamental types

Augment the description of `std::meta::info` found in paragraph §17 to add attribute as a valid representation to the current enumerated list

> A value of type
> 
> std​::​meta​::​info
> 
> is called a
> 
> reflection
> 
> . There exists a unique
> 
> null reflection
> 
> ; every other reflection is a representation of
> 
> ...
> 
> — a data member description ([class.mem.general]), or
> 
> — an attribute ([dcl.attr])

Update `Recommended practices` in paragraph §18 to remove attributes from the list

> Recommended practice
> 
> : Implementations should not represent other constructs specified in this document, such as
> 
> using-declarators
> 
> , partial template specializations,
> 
> attributes,
> 
> placeholder types, statements, or expressions, as values of type
> 
> std​::​meta​::​info
> 
> .

#### [expr.reflect] The reflection operator

Edit *reflect-expression* production rule to support reflecting over attributes

> reflect-expression
> 
> :
> 
> ^^
> 
> ::
> 
> ^^
> 
> reflection-name
> 
> ^^
> 
> type-id
> 
> ^^
> 
> id-expression
> 
> `^^ [[` *attribute* `]]`

Add a new paragraph at the bottom [expr.reflect] to describe the new rule `^^[[` *attribute* `]]`

>  A *reflect-expression* of the form `^^[[ attribute ]]` for attribute described in this document [dcl.attr], represents said attribute. For an *attribute* `r` with *attribute-token* `assume` [dcl.attr.assume], computing the reflection of `r` is ill-formed. For an *attribute* `r` non described in this document, computing the reflection of `r` is ill-formed absent implementation-defined guarantees with respect to said *attribute*.

#### [expr.eq] Equality Operators

Update 7.6.10 [expr.eq] paragraph §6 to add a clause for comparing reflection of attributes

> ...
> 
> - (6.7) represent equal data member descriptions ([class.mem.general]),
> - (6.7+) represent identical attribute ([dcl.attr])
> 
> [*Example:*
> 
> ```
> static_assert(^^[[nodiscard]] == ^^[[nodiscard]]);
> static_assert(^^[[nodiscard("keep")]] == ^^[[nodiscard("keep")]]);
> static_assert(^^[[nodiscard]] != ^^[[deprecated]]);
> static_assert(^^[[nodiscard("keep")]] != ^^[[nodiscard("keep too")]]);
> static_assert(^^[[nodiscard("keep")]] != ^^[[nodiscard]]);
> ```
> 
> — *end example*]
> 
> and they compare unequal otherwise.

#### [dcl.attr.grammar] Attribute syntax and semantics

Add a new paragraph at the end to describe when are two attributes considered identical. We compare the attribute tokens which must match, and their clause for simple clause.

> For any two attributes
> 
> r
> 
> 1
> 
> and
> 
> r
> 
> 2
> 
> ,
> 
> r
> 
> 1
> 
> and
> 
> r
> 
> 2
> 
> are identical if their
> 
> attribute-token
> 
> are identical and
> 
> -
> 
> r
> 
> 1
> 
> and
> 
> r
> 
> 2
> 
> accept no
> 
> attribute-argument-clause
> 
> , or
> 
> -
> 
> r
> 
> 1
> 
> and
> 
> r
> 
> 2
> 
> accept optional
> 
> attribute-argument-clause
> 
> and they are both empty or
> 
> -
> 
> r
> 
> 1
> 
> and
> 
> r
> 
> 2
> 
> accept
> 
> attribute-argument-clause
> 
> of the form
> 
> (
> 
> type-id
> 
> )
> 
> and the
> 
> type-id
> 
> s denote the same type or
> 
> -
> 
> r
> 
> 1
> 
> and
> 
> r
> 
> 2
> 
> accept
> 
> attribute-argument-clause
> 
> of the form
> 
> (
> 
> unevaluated-string
> 
> )
> 
> and
> 
> balanced-token-seq
> 
> s of
> 
> r
> 
> 1
> 
> and
> 
> r
> 
> 2
> 
> are identical.
> 
> Otherwise *r*<sub>1</sub> and *r*<sub>2</sub> are not identical. (*Note*: Identity between attributes not described in this document is implementation defined)

### Library

#### [meta.syn] Header <meta> synopsis

Add to the [meta.reflection.queries] section from the synopsis, the metafunctions `is_attribute`, `attributes_of` and `has_attribute` along with the `attribute_comparison` enumeration.

> ```
> namespace std::meta {
>   // ... [meta.reflection.queries], reflection queries ...
> 
>   consteval bool is_attribute(info r);
> 
>   consteval vector<info> attributes_of(info r);
> 
>   enum class attribute_comparison {
>     ignore_namespace,
>     ignore_argument,
>   };
> 
>   consteval bool has_attribute(info r, info a);
> 
>   consteval bool has_attribute(info r, info a, attribute_comparison flags);
> 
> }
> ```

#### [meta.reflection.names] Reflection names and locations

Introduce a subclause to `has_identifier` describing the return value to be `true` for attribute reflection. Renumber the last clause appropriately.

> consteval bool has_identifier(info r);
> 
> [1] *Returns*: ... (1.13+) — Otherwise, if r represents an attribute, then `true`

Introduce a subclause to `identifier_of`, `u8identifier_of`, describing the return value of attribute reflection to be the `attribute-token`. Renumber the last clause appropriately.

> consteval string_view identifier_of(info r);
> 
> consteval u8string_view u8identifier_of(info r);
> 
> [3] *Returns*: An NTMBS, encoded with *E*, determined as follows: ...
> 
> — Otherwise, if `r` represents an attribute `a`, then the *attribute-token* of `a`

#### [meta.reflection.queries] Reflection queries

Add the new clauses to support new metafunctions, and the new enumeration.

> `consteval bool is_attribute(info r);`
> 
> *Returns*: `true` if `r` represents an attribute. Otherwise, `false`.
> 
> `consteval vector<info> attributes_of(info r);`
> 
> *Returns*: A vector `v` containing reflections of all attributes appertaining to the entity represented by `r`, such that `is_attribute(v`<sub>i</sub>`)` is true for every attribute v<sub>i</sub> in `v`. The ordering of `v` is unspecified.

Add a new table to describe the comparison policy `attribute_comparison` between attributes. Add a new clause to describe `has_attribute`

> ```
> enum class attribute_comparison {
>   ignore_namespace = unspecified,
>   ignore_argument = unspecified,
> };
> ```
> 
> The type `attribute_comparison` is an implementation-defined bitmask type ([bitmask.types]). Setting its elements has the effect listed in Table (*) [tab:meta.reflection.queries]
> 
> `attribute_comparison` effects [tab:meta.reflection.queries]
> 
> <!-- tomd:lossy-table -->
> | Element | Effect(s) if set |
> | --- | --- |
> | ignore_namespace | Specifies that the attribute-namespace is ignored when comparing attributes |
> | ignore_argument | Specifies that the attribute-argument-clause is ignored when comparing attributes |
> 
> `consteval bool has_attribute(info r, info a);`
> 
> *Returns*: True if `a` was found appertaining to the construct `r`.
> 
> *Throws*: `meta::exception` unless `is_attribute(a)` is `true`.
> 
> `consteval bool has_attribute(info r, info a, attribute_comparison flags);`
> 
> *Returns*: True if `a` was found appertaining to the construct `r`. The bitmasks specified in `flags` determine which components of an attribute are considered significant for matching purpose.
> 
> *Throws*: `meta::exception` unless `is_attribute(a)` is `true`.

#### [meta.reflection.define.aggregate] Reflection class definition generation

Change `data_member_options` definition to deprecate `no_unique_address`, and add the `attributes` data member.

```
    namespace std::meta {
      struct data_member_options {
        struct name-type {
          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;
        };

        optional<name-type> name;
        optional<int> alignment;
        optional<int> bit_width;
        bool no_unique_address = false;
        [[deprecated("Use .attributes")]] bool no_unique_address = false;
        vector<info> attributes = {};
        vector<info> annotations;

      };
    }
```

Describe the contribution from this new member in

Returns

component.

> Returns: A reflection of a data member description (T, N, A, W, NUA, ANN
> 
> , AT
> 
> ) (11.4.1 [class.mem.general]) where
> 
> ...
> 
> - AT is the value held by
> 
> options.attributes
> 
> .

Describe the new `attributes` member effect on `define_aggregate`

> Let C be the type represented by class_type and r
> 
> k
> 
> be the Kth reflection value in mdescrs. For every r
> 
> k
> 
> in mdescrs, let (T
> 
> K
> 
> , N
> 
> K
> 
> , A
> 
> K
> 
> , W
> 
> K
> 
> , NUA
> 
> K
> 
> , ANN
> 
> K
> 
> , AT<sub>K</sub>
> 
> ) be the corresponding data member description represented by r
> 
> k
> 
> .

> Constants When:
> 
> ...
> 
> - For every
> 
> r<sub>k</sub>
> 
> in
> 
> AT<sub>k</sub>
> 
> ,
> 
> is_attribute(r
> 
> k
> 
> )
> 
> is true for every k

> Effects: Produces an injected declaration D ([expr.const]) that defines C and has properties as follows:
> 
> ...
> 
> For every attribute reflection
> 
> r
> 
> in AT
> 
> k
> 
> , r appertains to M
> 
> k
> 
> ...

### Feature-test macro

The attribute reflection feature is guarded behind a macro. Augment 15.12 [cpp.predefined]

> __cpp_impl_reflection_attributes 2026XXL

## Feedback

### Poll

#### P3385R1: SG7, Nov 2024, WG21 meetings in Wroclaw

- SG7 encourages more work on reflection of attributes as described in the paper: No objection to unanimous consent

#### P3385R2: SG7, Dec 2024, Telecon

- SG7 wants to support namespaced attributes: No objection to unanimous consent.
- SG7 wants to support "easy" arguments of attributes: No objection to unanimous consent.
- SG7 wants to support arguments (full expressions) of attributes: Not consensus.
- SG7 considers the paper high-priority and forwards it to LEWG and EWG for C++26: Not consensus.
- SG7 forwards this paper to LEWG and EWG for C++29: Not consensus.

#### P3385R3: SG7, Feb 2025, Hagenberg

- SG7 wants to support token source type arguments: Not consensus
- SG7 wants to forward to EWG and LEWG as is: Consensus

#### P3385R6: SG7/EWG, June 2025, Sofia

- SG7 wants to allow arbitrary attributes support via define_aggregate: Consensus (recommendation to merge into P3385)
- EWG would prefer to see this paper without the ability to appertain an attribute to an entity : Consensus
- EWG encourages more work in the direction of the paper that better exposes the details of the attributes from a querying perspective: Consensus
- EWG encourages more work on reflecting attributes in the direction of the paper: Not consensus

### Implementation

The features presented here are available on compiler explorer [2].

---

[1]: It is mostly an academic question since `[[assume]]` can only appertain to the null statement, no calls to `attributes_of` could return such a reflection. The only way to get one is to construct one explicitly via `constexpr auto r = ^^[[assume(expr)]];` and the utility of doing so is null.

[2]: [Compiler explorer](https://godbolt.org/z/MKEh9jjbP)

## References

### Non-Normative References

**[P2237R0]
   Andrew Sutton. [Metaprogramming](https://wg21.link/p2237r0). 15 October 2020. URL: [https://wg21.link/p2237r0](https://wg21.link/p2237r0)
[P2996R11]
   Barry Revzin, Wyatt Childers, Peter Dimov, Andrew Sutton, Faisal Vali, Daveed Vandevoorde, Dan Katz. [Reflection for C++26](https://wg21.link/p2996r11). 16 April 2025. URL: [https://wg21.link/p2996r11](https://wg21.link/p2996r11)
[P2996R9]
   Barry Revzin, Wyatt Childers, Peter Dimov, Andrew Sutton, Faisal Vali, Daveed Vandevoorde, Dan Katz. [Reflection for C++26](https://wg21.link/p2996r9). 13 January 2025. URL: [https://wg21.link/p2996r9](https://wg21.link/p2996r9)
[P3385R0]
   Aurelien Cassagnes, Aurelien Cassagnes, Roman Khoroshikh, Anders Johansson. [Attributes reflection](https://wg21.link/p3385r0). 16 September 2024. URL: [https://wg21.link/p3385r0](https://wg21.link/p3385r0)
[P3385R1]
   Aurelien Cassagnes, Roman Khoroshikh, Anders Johansson. [Attributes reflection](https://wg21.link/p3385r1). 15 October 2024. URL: [https://wg21.link/p3385r1](https://wg21.link/p3385r1)
[P3385R2]
   Aurelien Cassagnes, Roman Khoroshikh, Anders Johansson. [Attributes reflection](https://wg21.link/p3385r2). 12 December 2024. URL: [https://wg21.link/p3385r2](https://wg21.link/p3385r2)
[P3385R3]
   Aurelien Cassagnes, Roman Khoroshikh, Anders Johansson. [Attributes reflection](https://wg21.link/p3385r3). 7 January 2025. URL: [https://wg21.link/p3385r3](https://wg21.link/p3385r3)
[P3385R4]
   Aurelien Cassagnes. [Attributes reflection](https://wg21.link/p3385r4). 11 March 2025. URL: [https://wg21.link/p3385r4](https://wg21.link/p3385r4)
[P3385R5]
   Aurelien Cassagnes. [Attributes reflection](https://wg21.link/p3385r5). 19 May 2025. URL: [https://wg21.link/p3385r5](https://wg21.link/p3385r5)
[P3385R6]
   Aurelien Cassagnes. [Attributes reflection](https://wg21.link/p3385r6). 26 September 2025. URL: [https://wg21.link/p3385r6](https://wg21.link/p3385r6)
[P3678R0]
   Aurelien Cassagnes. [Arbitrary attributes in define_aggregate](https://wg21.link/p3678r0). 15 May 2025. URL: [https://wg21.link/p3678r0](https://wg21.link/p3678r0)
[P4033R0]
   Aurelien Cassagnes. [Synthesizing enum at compile time with define_enum](https://wg21.link/p4033r0). 13 April 2026. URL: [https://wg21.link/p4033r0](https://wg21.link/p4033r0)**
