Indeed, the current model is very consistent. The requested change is not motivated by ideas of philosophical purity or the like, but rather by very practical implications.

The first issue is safety. Reflection of program metadata (not involving splicing), which by necessity happens entirely at compile time, is always memory safe. Splicing of non-public members on the other hand is a fundamentally unsafe operation. The access controls protect class invariants from being invalidated by external actors, and they protect external actors from concurrent access happening within the class. Violating assumptions through either route leads to undefined behaviour.

A good demonstration of the fundamental unsafety of access breaking comes from Rust. If such a feature existed in Rust, it would have to be considered unsafe, lest the entire borrow checker be made unsound. In a time when safety is more important than ever, C++ should not be introducing new implicitly unsafe features.

The second issue is coupling. Allowing access to implementation increases coupling between libraries. This has been found detrimental time and again by other comparable programming language ecosystems. In most cases it has been chosen to restrict access after users inevitably abuse it. Some examples: Java (Project Jigsaw, JEP 260, JEP 396, JEP 403, ...), Go (//go:linkname directives restricted in version 1.23), JavaScript (ES2022 introduced #private members). .NET is notable in that they chose to go the other way and embrace access breaking. In .NET 8 a compiler attribute called [UnsafeAccessor] was introduced for this very purpose.

If WG21 considers access breaking an important feature, then it should be explicit and auditable, as .NET has chosen to do. Note that access_context::unchecked() is insufficient, because - as noted above - access to non-public program metadata is not in itself unsafe, nor is it likely to introduce much greater coupling than what already exists due to type traits and ABI.

Finally, it should be understood that access breaking is not necessary for flagship reflection use cases such as serialization, because any such use case requires cooperation between the serializer and the class being serialized. This is because access controls imply invariants and given an arbitrary class, it is impossible to deduce those invariants through reflection. Most likely that cooperation will take the form of annotations provided by the serialization library. Given that cooperation is necessary, it is not an undue burden for such a class to also befriend some part of the serialization library, as libraries have done for decades (see e.g. boost::serialization::access).

- Lauri Ojell-Vasama

On 10/11/2026 16.43, Tomasz Kamiński via SG7 wrote:


wt., 10 lis 2026 o 15:10 Tomasz Kamiński <tomaszkam@gmail.com> napisał(a):


sob., 7 lis 2026 o 23:38 Hana Dusíková via Ext <ext@lists.isocpp.org> napisał(a):
Dear SG7 kidnapees, school-yard bullies, nerds, innocent bystanders, mountaineers,

we are going to have an 4FA SG7 telecon this Wednesday, on zoom:

https://iso.zoom.us/j/95914491492 the password is not part of the link, and you must do some self-reflection and type it o your own: [:^^_:][_]

Good luck!

The meeting starts and ends depending on your spatial and temporal location:

18:00 - 20:00 CET 🇪🇺
17:00 - 19:00 UTC 💂🏿‍♀️
19:00 - 21:00 EET 🗡️
20-00 - 22:00 MSK🪆
09:00am - 11:00am PST 🌁
10:00am - noon MST 🏔️
noon - 2pm EST 🗽
02:00am - 04:00am JST 👘
10:30pm - 00:30am IST 🪷
07:00am - 09:00am HST vo🌋

(it's also in the calendar)

sidenote: I need a scribe! ⚠️

We are going to discuss following DIS comments:

Ville, would be possible for you to  provide me a more context here, especially to answer following question by consulting with Finland NB/comemnt author.

I am mostly interesting in the author reasoning for providing a different rules for obj.*private-memeber-pointer and obj.[: private _reflection :].
I see this two as parallel, as in both situation we are using handle representing an concrete member, and not performing it's lookup.
And in rest of cpluplus the access check is consistently attached to lookup and not other usages, for example (https://godbolt.org/z/Paoh16rE1):
class X {
   struct Y { ... };
public:
   X foo();
The return here of foo() should be 'Y' not 'X`, as otherwise the example fail to convey my point. 
};

X::Y x; // ill-formed, Y is private
decltype(std::declval<X&>().foo()) x; // OK, the name is private, but class not

I find the current model to be very consistent with the rest of the language, the access check is performed when forming an reflection
handle (^^X::Y or passing right member context to members_of), but afterwards no checks is performed. Without a very strong motivation,
I would be opposed to deviating to above model.


 

And these are all related papers, or papers proposing the fixes:

  • P4414R0 (Ville's paper from mailing which proposed special context specific keyword)
  • P4340R0 (Barry's paper from September mailing for c++26 providing generic and customizable solution) (there is a newer version here: D4341R1 (contains wording!)
  • D4442R0 (Ville's paper summarizing SG7 Mattermost discussion, proposing future compatible way with P4340 without customization, only as opt-in/opt-out for structurality of types)
  • P3687R1 (Dan's paper from Sofia which proposed way how to handle using-declaration, but we decided to make them ill-formed, in order to find a good solution later)
  • There is no paper for the splicing (the third comment) AFAIK, this will be discussed in EWG week later in Búzios

I probably forgot some, so if anyone working on these please send them to this thread.


And I think that's all.

Hana

_______________________________________________
Ext mailing list
Ext@lists.isocpp.org
Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/ext
Link to this post: http://lists.isocpp.org/ext/2026/11/28443.php