<!doctype html><html lang="en">
 <head>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  <meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
  <title>P3587R0: Reconsider reflection access for C++26</title>
<style data-fill-with="stylesheet">/******************************************************************************
 *                   Style sheet for the W3C specifications                   *
 *
 * Special classes handled by this style sheet include:
 *
 * Indices
 *   - .toc for the Table of Contents (<ol class="toc">)
 *     + <span class="secno"> for the section numbers
 *   - #toc for the Table of Contents (<nav id="toc">)
 *   - ul.index for Indices (<a href="#ref">term</a><span>, in § N.M</span>)
 *   - table.index for Index Tables (e.g. for properties or elements)
 *
 * Structural Markup
 *   - table.data for general data tables
 *     -> use 'scope' attribute, <colgroup>, <thead>, and <tbody> for best results !
 *     -> use <table class='complex data'> for extra-complex tables
 *     -> use <td class='long'> for paragraph-length cell content
 *     -> use <td class='pre'> when manual line breaks/indentation would help readability
 *   - dl.switch for switch statements
 *   - ol.algorithm for algorithms (helps to visualize nesting)
 *   - .figure and .caption (HTML4) and figure and figcaption (HTML5)
 *     -> .sidefigure for right-floated figures
 *   - ins/del
 *     -> ins/del.c### for candidate and proposed changes (amendments)
 *
 * Code
 *   - pre and code
 *
 * Special Sections
 *   - .note       for informative notes             (div, p, span, aside, details)
 *   - .example    for informative examples          (div, p, pre, span)
 *   - .issue      for issues                        (div, p, span)
 *   - .advisement for loud normative statements     (div, p, strong)
 *   - .annoying-warning for spec obsoletion notices (div, aside, details)
 *   - .correction for "candidate corrections"       (div, aside, details, section)
 *   - .addition   for "candidate additions"         (div, aside, details, section)
 *   - .correction.proposed for "proposed corrections" (div, aside, details, section)
 *   - .addition.proposed   for "proposed additions"   (div, aside, details, section)
 *
 * Definition Boxes
 *   - pre.def   for WebIDL definitions
 *   - table.def for tables that define other entities (e.g. CSS properties)
 *   - dl.def    for definition lists that define other entitles (e.g. HTML elements)
 *
 * Numbering
 *   - .secno for section numbers in .toc and headings (<span class='secno'>3.2</span>)
 *   - .marker for source-inserted example/figure/issue numbers (<span class='marker'>Issue 4</span>)
 *   - ::before styled for CSS-generated issue/example/figure numbers:
 *     -> Documents wishing to use this only need to add
 *        figcaption::before,
 *        .caption::before { content: "Figure "  counter(figure) " ";  }
 *        .example::before { content: "Example " counter(example) " "; }
 *        .issue::before   { content: "Issue "   counter(issue) " ";   }
 *
 * Header Stuff (ignore, just don't conflict with these classes)
 *   - .head for the header
 *   - .copyright for the copyright
 *
 * Outdated warning for old specs
 *
 * Miscellaneous
 *   - .overlarge for things that should be as wide as possible, even if
 *     that overflows the body text area. This can be used on an item or
 *     on its container, depending on the effect desired.
 *     Note that this styling basically doesn't help at all when printing,
 *     since A4 paper isn't much wider than the max-width here.
 *     It's better to design things to fit into a narrower measure if possible.
 *
 *   - js-added ToC jump links (see fixup.js)
 *
 ******************************************************************************/

/* color variables included separately for reliability */

/******************************************************************************/
/*                                    Body                                    */
/******************************************************************************/

	html {
	}

	body {
		counter-reset: example figure issue;

		/* Layout */
		max-width: 50em;			  /* limit line length to 50em for readability   */
		margin: 0 auto;				/* center text within page                    */
		padding: 1.6em 1.5em 2em 50px; /* assume 16px font size for downlevel clients */
		padding: 1.6em 1.5em 2em calc(26px + 1.5em); /* leave space for status flag    */

		/* Typography */
		line-height: 1.5;
		font-family: sans-serif;
		widows: 2;
		orphans: 2;
		word-wrap: break-word;
		overflow-wrap: break-word;
		hyphens: auto;

		color: black;
		color: var(--text);
		background: white top left fixed no-repeat;
		background: var(--bg) top left fixed no-repeat;
		background-size: 25px auto;
	}


/******************************************************************************/
/*                         Front Matter & Navigation                          */
/******************************************************************************/

/** Header ********************************************************************/

	div.head { margin-bottom: 1em; }
	div.head hr { border-style: solid; }

	div.head h1 {
		font-weight: bold;
		margin: 0 0 .1em;
		font-size: 220%;
	}

	div.head h2 { margin-bottom: 1.5em;}

/** W3C Logo ******************************************************************/

	.head .logo {
		float: right;
		margin: 0.4rem 0 0.2rem .4rem;
	}

	.head img[src*="logos/W3C"] {
		display: block;
		border: solid #1a5e9a;
		border: solid var(--logo-bg);
		border-width: .65rem .7rem .6rem;
		border-radius: .4rem;
		background: #1a5e9a;
		background: var(--logo-bg);
		color: white;
		color: var(--logo-text);
		font-weight: bold;
	}

	.head a:hover > img[src*="logos/W3C"],
	.head a:focus > img[src*="logos/W3C"] {
		opacity: .8;
	}

	.head a:active > img[src*="logos/W3C"] {
		background: #c00;
		background: var(--logo-active-bg);
		border-color: #c00;
		border-color: var(--logo-active-bg);
	}

	/* see also additional rules in Link Styling section */

/** Copyright *****************************************************************/

	p.copyright,
	p.copyright small { font-size: small; }

/** Back to Top / ToC Toggle **************************************************/

	@media print {
		#toc-nav {
			display: none;
		}
	}
	@media not print {
		#toc-nav {
			position: fixed;
			z-index: 3;
			bottom: 0; left: 0;
			margin: 0;
			min-width: 1.33em;
			border-top-right-radius: 2rem;
			box-shadow: 0 0 2px;
			font-size: 1.5em;
		}
		#toc-nav > a {
			display: block;
			white-space: nowrap;

			height: 1.33em;
			padding: .1em 0.3em;
			margin: 0;

			box-shadow: 0 0 2px;
			border: none;
			border-top-right-radius: 1.33em;

			color: #707070;
			color: var(--tocnav-normal-text);
			background: white;
			background: var(--tocnav-normal-bg);
		}
		#toc-nav > a:hover,
		#toc-nav > a:focus {
			color: black;
			color: var(--tocnav-hover-text);
			background: #f8f8f8;
			background: var(--tocnav-hover-bg);
		}
		#toc-nav > a:active {
			color: #c00;
			color: var(--tocnav-active-text);
			background: white;
			background: var(--tocnav-active-bg);
		}

		#toc-nav > #toc-jump {
			padding-bottom: 2em;
			margin-bottom: -1.9em;
		}

		/* statusbar gets in the way on keyboard focus; remove once browsers fix */
		#toc-nav > a[href="#toc"]:not(:hover):focus:last-child {
			padding-bottom: 1.5rem;
		}

		#toc-nav:not(:hover) > a:not(:focus) > span + span {
			/* Ideally this uses :focus-within on #toc-nav */
			display: none;
		}
		#toc-nav > a > span + span {
			padding-right: 0.2em;
		}
	}

/** ToC Sidebar ***************************************************************/

	/* Floating sidebar */
	@media screen {
		body.toc-sidebar #toc {
			position: fixed;
			top: 0; bottom: 0;
			left: 0;
			width: 23.5em;
			max-width: 80%;
			max-width: calc(100% - 2em - 26px);
			overflow: auto;
			padding: 0 1em;
			padding-left: 42px;
			padding-left: calc(1em + 26px);
			color: black;
			color: var(--tocsidebar-text);
			background: inherit;
			background-color: #f7f8f9;
			background-color: var(--tocsidebar-bg);
			z-index: 1;
			box-shadow: -.1em 0 .25em rgba(0,0,0,.1) inset;
			box-shadow: -.1em 0 .25em var(--tocsidebar-shadow) inset;
		}
		body.toc-sidebar #toc h2 {
			margin-top: .8rem;
			font-variant: small-caps;
			font-variant: all-small-caps;
			text-transform: lowercase;
			font-weight: bold;
			color: gray;
			color: hsla(203,20%,40%,.7);
			color: var(--tocsidebar-heading-text);
		}
		body.toc-sidebar #toc-jump:not(:focus) {
			width: 0;
			height: 0;
			padding: 0;
			position: absolute;
			overflow: hidden;
		}
	}
	/* Hide main scroller when only the ToC is visible anyway */
	@media screen and (max-width: 28em) {
		body.toc-sidebar {
			overflow: hidden;
		}
	}

	/* Sidebar with its own space */
	@media screen and (min-width: 78em) {
		body:not(.toc-inline) #toc {
			position: fixed;
			top: 0; bottom: 0;
			left: 0;
			width: 23.5em;
			overflow: auto;
			padding: 0 1em;
			padding-left: 42px;
			padding-left: calc(1em + 26px);
			color: black;
			color: var(--tocsidebar-text);
			background: inherit;
			background-color: #f7f8f9;
			background-color: var(--tocsidebar-bg);
			z-index: 1;
			box-shadow: -.1em 0 .25em rgba(0,0,0,.1) inset;
			box-shadow: -.1em 0 .25em var(--tocsidebar-shadow) inset;
		}
		body:not(.toc-inline) #toc h2 {
			margin-top: .8rem;
			font-variant: small-caps;
			font-variant: all-small-caps;
			text-transform: lowercase;
			font-weight: bold;
			color: gray;
			color: hsla(203,20%,40%,.7);
			color: var(--tocsidebar-heading-text);
		}

		body:not(.toc-inline) {
			padding-left: 29em;
		}
		/* See also Overflow section at the bottom */

		body:not(.toc-inline) #toc-jump:not(:focus) {
			width: 0;
			height: 0;
			padding: 0;
			position: absolute;
			overflow: hidden;
		}
	}
	@media screen and (min-width: 90em) {
		body:not(.toc-inline) {
			margin: 0 4em;
		}
	}

/******************************************************************************/
/*                                Sectioning                                  */
/******************************************************************************/

/** Headings ******************************************************************/

	h1, h2, h3, h4, h5, h6, dt {
		page-break-after: avoid;
		page-break-inside: avoid;
		font: 100% sans-serif;   /* Reset all font styling to clear out UA styles */
		font-family: inherit;	/* Inherit the font family. */
		line-height: 1.2;		/* Keep wrapped headings compact */
		hyphens: manual;		/* Hyphenated headings look weird */
	}

	h2, h3, h4, h5, h6 {
		margin-top: 3rem;
	}

	h1, h2, h3 {
		color: #005A9C;
		color: var(--heading-text);
	}

	h1 { font-size: 170%; }
	h2 { font-size: 140%; }
	h3 { font-size: 120%; }
	h4 { font-weight: bold; }
	h5 { font-style: italic; }
	h6 { font-variant: small-caps; }
	dt { font-weight: bold; }

/** Subheadings ***************************************************************/

	h1 + h2,
	#profile-and-date {
		/* #profile-and-date is a subtitle in an H2 under the H1 */
		margin-top: 0;
	}
	h2 + h3,
	h3 + h4,
	h4 + h5,
	h5 + h6 {
		margin-top: 1.2em; /* = 1 x line-height */
	}

/** Section divider ***********************************************************/

	:not(.head) > :not(.head) + hr {
		font-size: 1.5em;
		text-align: center;
		margin: 1em auto;
		height: auto;
		color: black;
		color: var(--hr-text);
		border: transparent solid 0;
		background: transparent;
	}
	:not(.head) > hr::before {
		content: "\2727\2003\2003\2727\2003\2003\2727";
	}

/******************************************************************************/
/*                            Paragraphs and Lists                            */
/******************************************************************************/

	p {
		margin: 1em 0;
	}

	dd > p:first-child,
	li > p:first-child {
		margin-top: 0;
	}

	ul, ol {
		margin-left: 0;
		padding-left: 2em;
	}

	li {
		margin: 0.25em 0 0.5em;
		padding: 0;
	}

	dl dd {
		margin: 0 0 .5em 2em;
	}

	.head dd + dd { /* compact for header */
		margin-top: -.5em;
	}

	/* Style for algorithms */
	ol.algorithm ol:not(.algorithm),
	.algorithm > ol ol:not(.algorithm) {
	border-left: 0.5em solid #DEF;
	border-left: 0.5em solid var(--algo-border);
	}

	/* Put nice boxes around each algorithm. */
	[data-algorithm]:not(.heading) {
	 padding: .5em;
	 border: thin solid #ddd;
	 border: thin solid var(--algo-border);
	 border-radius: .5em;
	 margin: .5em calc(-0.5em - 1px);
	}
	[data-algorithm]:not(.heading) > :first-child {
	 margin-top: 0;
	}
	[data-algorithm]:not(.heading) > :last-child {
	 margin-bottom: 0;
	}

	/* Style for switch/case <dl>s */
	dl.switch > dd > ol.only,
	dl.switch > dd > .only > ol {
	margin-left: 0;
	}
	dl.switch > dd > ol.algorithm,
	dl.switch > dd > .algorithm > ol {
	margin-left: -2em;
	}
	dl.switch {
	padding-left: 2em;
	}
	dl.switch > dt {
	text-indent: -1.5em;
	margin-top: 1em;
	}
	dl.switch > dt + dt {
	margin-top: 0;
	}
	dl.switch > dt::before {
	content: '\21AA';
	padding: 0 0.5em 0 0;
	display: inline-block;
	width: 1em;
	text-align: right;
	line-height: 0.5em;
	}

/** Terminology Markup ********************************************************/


/******************************************************************************/
/*                                 Inline Markup                              */
/******************************************************************************/

/** Terminology Markup ********************************************************/
	dfn   { /* Defining instance */
		font-weight: bolder;
	}
	a > i { /* Instance of term */
		font-style: normal;
	}
	dt dfn code, code.idl {
		font-size: inherit;
	}
	dfn var {
		font-style: normal;
	}

/** Change Marking ************************************************************/

	del {
		color: #aa0000;
		color: var(--del-text);
		background: transparent;
		background: var(--del-bg);
		text-decoration: line-through;
	}
	ins {
		color: #006100;
		color: var(--ins-text);
		background: transparent;
		background: var(--ins-bg);
		text-decoration: underline;
	}

	/* for amendments (candidate/proposed changes) */

	.amendment ins, .correction ins, .addition ins,
	ins[class^=c] {
		text-decoration-style: dotted;
	}
	.amendment del, .correction del, .addition del,
	del[class^=c] {
		text-decoration-style: dotted;
	}
	.amendment.proposed ins, .correction.proposed ins, .addition.proposed ins,
	ins[class^=c].proposed {
		text-decoration-style: double;
	}
	.amendment.proposed del, .correction.proposed del, .addition.proposed del,
	del[class^=c].proposed {
		text-decoration-style: double;
	}

/** Miscellaneous improvements to inline formatting ***************************/

	sup {
		vertical-align: super;
		font-size: 80%
	}

/******************************************************************************/
/*                                    Code                                    */
/******************************************************************************/

/** General monospace/pre rules ***********************************************/

	pre, code, samp {
		font-family: Menlo, Consolas, "DejaVu Sans Mono", Monaco, monospace;
		font-size: .9em;
		hyphens: none;
		text-transform: none;
		text-align: left;
		text-align: start;
		font-variant: normal;
		orphans: 3;
		widows: 3;
		page-break-before: avoid;
	}
	pre code,
	code code {
		font-size: 100%;
	}

	pre {
		margin-top: 1em;
		margin-bottom: 1em;
		overflow: auto;
	}

/** Inline Code fragments *****************************************************/

	/* Do something nice. */

/******************************************************************************/
/*                                    Links                                   */
/******************************************************************************/

/** General Hyperlinks ********************************************************/

	/* We hyperlink a lot, so make it less intrusive */
	a[href] {
		color: #034575;
		color: var(--a-normal-text);
		text-decoration: underline #707070;
		text-decoration: underline var(--a-normal-underline);
		text-decoration-skip-ink: none;
	}
	a:visited {
		color: #034575;
		color: var(--a-visited-text);
		text-decoration-color: #bbb;
		text-decoration-color: var(--a-visited-underline);
	}

	/* Indicate interaction with the link */
	a[href]:focus,
	a[href]:hover {
		text-decoration-thickness: 2px;
	}
	a[href]:active {
		color: #c00;
		color: var(--a-active-text);
		text-decoration-color: #c00;
		text-decoration-color: var(--a-active-underline);
	}

	/* Backout above styling for W3C logo */
	.head .logo,
	.head .logo a {
		border: none;
		text-decoration: none;
		background: transparent;
	}

/******************************************************************************/
/*                                    Images                                  */
/******************************************************************************/

	img {
		border-style: none;
	}

	img, svg {
		/* Intentionally not color-scheme aware. */
		background: white;
	}

	/* For autogen numbers, add
	  .caption::before, figcaption::before { content: "Figure " counter(figure) ". "; }
	*/

	figure, .figure, .sidefigure {
		page-break-inside: avoid;
		text-align: center;
		margin: 2.5em 0;
	}
	.figure img,	.sidefigure img,	figure img,
	.figure object, .sidefigure object, figure object {
		max-width: 100%;
		margin: auto;
		height: auto;
	}
	.figure pre, .sidefigure pre, figure pre {
		text-align: left;
		display: table;
		margin: 1em auto;
	}
	.figure table, figure table {
		margin: auto;
	}
	@media screen and (min-width: 20em) {
		.sidefigure {
			float: right;
			width: 50%;
			margin: 0 0 0.5em 0.5em;
		}
	}
	.caption, figcaption, caption {
		font-style: italic;
		font-size: 90%;
	}
	.caption::before, figcaption::before, figcaption > .marker {
		font-weight: bold;
	}
	.caption, figcaption {
		counter-increment: figure;
	}

	/* DL list is indented 2em, but figure inside it is not */
	dd > .figure, dd > figure { margin-left: -2em; }

/******************************************************************************/
/*                             Colored Boxes                                  */
/******************************************************************************/

	.issue, .note, .example, .assertion, .advisement, blockquote,
	.amendment, .correction, .addition {
		margin: 1em auto;
		padding: .5em;
		border: .5em;
		border-left-style: solid;
		page-break-inside: avoid;
	}
	span.issue, span.note {
		padding: .1em .5em .15em;
		border-right-style: solid;
	}

	blockquote > :first-child,
	.note  > p:first-child,
	.issue > p:first-child,
	.amendment > p:first-child,
	.correction > p:first-child,
	.addition > p:first-child {
		margin-top: 0;
	}
	blockquote > :last-child,
	.note  > p:last-child,
	.issue > p:last-child,
	.amendment > p:last-child,
	.correction > p:last-child,
	.addition > p:last-child {
		margin-bottom: 0;
	}


	.issue::before, .issue > .marker,
	.example::before, .example > .marker,
	.note::before, .note > .marker,
	details.note > summary > .marker,
	.amendment::before, .amendment > .marker,
	details.amendment > summary > .marker,
	.addition::before, .addition > .marker,
	addition.amendment > summary > .marker,
	.correction::before, .correction > .marker,
	correction.amendment > summary > .marker
	{
		text-transform: uppercase;
		padding-right: 1em;
	}

	.example::before, .example > .marker {
		display: block;
		padding-right: 0em;
	}

/** Blockquotes ***************************************************************/

	blockquote {
		border-color: silver;
		border-color: var(--blockquote-border);
		background: transparent;
		background: var(--blockquote-bg);
		color: currentcolor;
		color: var(--blockquote-text);
	}

/** Open issue ****************************************************************/

	.issue {
		border-color: #e05252;
		border-color: var(--issue-border);
		background: #fbe9e9;
		background: var(--issue-bg);
		color: black;
		color: var(--issue-text);
		counter-increment: issue;
		overflow: auto;
	}
	.issue::before, .issue > .marker {
		color: #831616;
		color: var(--issueheading-text);
	}
	/* Add .issue::before { content: "Issue " counter(issue) " "; } for autogen numbers,
	  or use class="marker" to mark up the issue number in source. */

/** Example *******************************************************************/

	.example {
		border-color: #e0cb52;
		border-color: var(--example-border);
		background: #fcfaee;
		background: var(--example-bg);
		color: black;
		color: var(--example-text);
		counter-increment: example;
		overflow: auto;
		clear: both;
	}
	.example::before, .example > .marker {
		color: #574b0f;
		color: var(--exampleheading-text);
	}
	/* Add .example::before { content: "Example " counter(example) " "; } for autogen numbers,
	  or use class="marker" to mark up the example number in source. */

/** Non-normative Note ********************************************************/

	.note {
		border-color: #52e052;
		border-color: var(--note-border);
		background: #e9fbe9;
		background: var(--note-bg);
		color: black;
		color: var(--note-text);
		overflow: auto;
	}

	.note::before, .note > .marker,
	details.note > summary {
		color: hsl(120, 70%, 30%);
		color: var(--noteheading-text);
	}
	/* Add .note::before { content: "Note "; } for autogen label,
	  or use class="marker" to mark up the label in source. */

	details.note[open] > summary {
		border-bottom: 1px silver solid;
		border-bottom: 1px var(--notesummary-underline) solid;
	}

/** Assertion Box *************************************************************/
	/*  for assertions in algorithms */

	.assertion {
		border-color: #AAA;
		border-color: var(--assertion-border);
		background: #EEE;
		background: var(--assertion-bg);
		color: black;
		color: var(--assertion-text);
	}

/** Advisement Box ************************************************************/
	/*  for attention-grabbing normative statements */

	.advisement {
		border-color: orange;
		border-color: var(--advisement-border);
		border-style: none solid;
		background: #fec;
		background: var(--advisement-bg);
		color: black;
		color: var(--advisement-text);
	}
	strong.advisement {
		display: block;
		text-align: center;
	}
	.advisement::before, .advisement > .marker {
		color: #b35f00;
		color: var(--advisementheading-text);
	}

/** Amendment Box *************************************************************/

	.amendment, .correction, .addition {
		border-color: #330099;
		border-color: var(--amendment-border);
		background: #F5F0FF;
		background: var(--amendment-bg);
		color: black;
		color: var(--amendment-text);
	}
	.amendment.proposed, .correction.proposed, .addition.proposed {
		border-style: solid;
		border-block-width: 0.25em;
	}
	.amendment::before, .amendment > .marker,
	details.amendment > summary::before, details.amendment > summary > .marker,
	.correction::before, .correction > .marker,
	details.correction > summary::before, details.correction > summary > .marker,
	.addition::before, .addition > .marker,
	details.addition > summary::before, details.addition > summary > .marker {
		color: #220066;
		color: var(--amendmentheading-text);
	}
	.amendment.proposed::before, .amendment.proposed > .marker,
	details.amendment.proposed > summary::before, details.amendment.proposed > summary > .marker,
	.correction.proposed::before, .correction.proposed > .marker,
	details.correction.proposed > summary::before, details.correction.proposed > summary > .marker,
	.addition.proposed::before, .addition.proposed > .marker,
	details.addition.proposed > summary::before, details.addition.proposed > summary > .marker {
		font-weight: bold;
	}

/** Spec Obsoletion Notice ****************************************************/
	/* obnoxious obsoletion notice for older/abandoned specs. */

	details {
		display: block;
	}
	summary {
		font-weight: bolder;
	}

	.annoying-warning:not(details),
	details.annoying-warning:not([open]) > summary,
	details.annoying-warning[open] {
		background: hsla(40,100%,50%,0.95);
		background: var(--warning-bg);
		color: black;
		color: var(--warning-text);
		padding: .75em 1em;
		border: red;
		border: var(--warning-border);
		border-style: solid none;
		box-shadow: 0 2px 8px black;
		text-align: center;
	}
	.annoying-warning :last-child {
		margin-bottom: 0;
	}

@media not print {
	details.annoying-warning[open] {
		position: fixed;
		left: 0;
		right: 0;
		bottom: 2em;
		z-index: 1000;
	}
}

	details.annoying-warning:not([open]) > summary {
		text-align: center;
	}

/** Entity Definition Boxes ***************************************************/

	.def {
		padding: .5em 1em;
		background: #def;
		background: var(--def-bg);
		margin: 1.2em 0;
		border-left: 0.5em solid #8ccbf2;
		border-left: 0.5em solid var(--def-border);
		color: black;
		color: var(--def-text);
	}

/******************************************************************************/
/*                                    Tables                                  */
/******************************************************************************/

	th, td {
		text-align: left;
		text-align: start;
	}

/** Property/Descriptor Definition Tables *************************************/

	table.def {
		/* inherits .def box styling, see above */
		width: 100%;
		border-spacing: 0;
	}

	table.def td,
	table.def th {
		padding: 0.5em;
		vertical-align: baseline;
		border-bottom: 1px solid #bbd7e9;
		border-bottom: 1px solid var(--defrow-border);
	}

	table.def > tbody > tr:last-child th,
	table.def > tbody > tr:last-child td {
		border-bottom: 0;
	}

	table.def th {
		font-style: italic;
		font-weight: normal;
		padding-left: 1em;
		width: 3em;
	}

	/* For when values are extra-complex and need formatting for readability */
	table td.pre {
		white-space: pre-wrap;
	}

	/* A footnote at the bottom of a def table */
	table.def td.footnote {
		padding-top: 0.6em;
	}
	table.def td.footnote::before {
		content: " ";
		display: block;
		height: 0.6em;
		width: 4em;
		border-top: thin solid;
	}

/** Data tables (and properly marked-up index tables) *************************/
	/*
		<table class="data"> highlights structural relationships in a table
		when correct markup is used (e.g. thead/tbody, th vs. td, scope attribute)

		Use class="complex data" for particularly complicated tables --
		(This will draw more lines: busier, but clearer.)

		Use class="long" on table cells with paragraph-like contents
		(This will adjust text alignment accordingly.)
		Alternately use class="longlastcol" on tables, to have the last column assume "long".
	*/

	table {
		word-wrap: normal;
		overflow-wrap: normal;
		hyphens: manual;
	}

	table.data,
	table.index {
		margin: 1em auto;
		border-collapse: collapse;
		border: hidden;
		width: 100%;
	}
	table.data caption,
	table.index caption {
		max-width: 50em;
		margin: 0 auto 1em;
	}

	table.data td,  table.data th,
	table.index td, table.index th {
		padding: 0.5em 1em;
		border-width: 1px;
		border-color: silver;
		border-color: var(--datacell-border);
		border-top-style: solid;
	}

	table.data thead td:empty {
		padding: 0;
		border: 0;
	}

	table.data  thead,
	table.index thead,
	table.data  tbody,
	table.index tbody {
		border-bottom: 2px solid;
	}

	table.data colgroup,
	table.index colgroup {
		border-left: 2px solid;
	}

	table.data  tbody th:first-child,
	table.index tbody th:first-child  {
		border-right: 2px solid;
		border-top: 1px solid silver;
		border-top: 1px solid var(--datacell-border);
		padding-right: 1em;
	}

	table.data th[colspan],
	table.data td[colspan] {
		text-align: center;
	}

	table.complex.data th,
	table.complex.data td {
		border: 1px solid silver;
		border: 1px solid var(--datacell-border);
		text-align: center;
	}

	table.data.longlastcol td:last-child,
	table.data td.long {
		vertical-align: baseline;
		text-align: left;
	}

	table.data img {
		vertical-align: middle;
	}


/*
Alternate table alignment rules

	table.data,
	table.index {
		text-align: center;
	}

	table.data  thead th[scope="row"],
	table.index thead th[scope="row"] {
		text-align: right;
	}

	table.data  tbody th:first-child,
	table.index tbody th:first-child  {
		text-align: right;
	}

Possible extra rowspan handling

	table.data  tbody th[rowspan]:not([rowspan='1']),
	table.index tbody th[rowspan]:not([rowspan='1']),
	table.data  tbody td[rowspan]:not([rowspan='1']),
	table.index tbody td[rowspan]:not([rowspan='1']) {
		border-left: 1px solid silver;
	}

	table.data  tbody th[rowspan]:first-child,
	table.index tbody th[rowspan]:first-child,
	table.data  tbody td[rowspan]:first-child,
	table.index tbody td[rowspan]:first-child{
		border-left: 0;
		border-right: 1px solid silver;
	}
*/

/******************************************************************************/
/*                                  Indices                                   */
/******************************************************************************/


/** Table of Contents *********************************************************/

	.toc a {
		/* More spacing; use padding to make it part of the click target. */
		padding: 0.1rem 1px 0;
		/* Larger, more consistently-sized click target */
		display: block;
		/* Switch to using border-bottom for underlines */
		text-decoration: none;
		border-bottom: 1px solid;
		/* Reverse color scheme */
		color: black;
		color: var(--toclink-text);
		border-color: #3980b5;
		border-color: var(--toclink-underline);
	}
	.toc a:visited {
		color: black;
		color: var(--toclink-visited-text);
		border-color: #054572;
		border-color: var(--toclink-visited-underline);
	}
	.toc a:focus,
	.toc a:hover {
		background: rgba(75%, 75%, 75%, .25);
		background: var(--a-hover-bg);
		border-bottom-width: 3px;
		margin-bottom: -2px;
	}
	.toc a:not(:focus):not(:hover) {
		/* Allow colors to cascade through from link styling */
		border-bottom-color: transparent;
	}

	.toc, .toc ol, .toc ul, .toc li {
		list-style: none; /* Numbers must be inlined into source */
		/* because generated content isn't search/selectable and markers can't do multilevel yet */
		margin:  0;
		padding: 0;
	}
	.toc {
		line-height: 1.1em;
	}

	/* ToC not indented until third level, but font style & margins show hierarchy */
	.toc > li			{ font-weight: bold;   }
	.toc > li li		 { font-weight: normal; }
	.toc > li li li	  { font-size:   95%;	}
	.toc > li li li li	{ font-size:   90%;	}
	.toc > li li li li li { font-size:   85%;	}

	/* @supports not (display:grid) { */
		.toc > li			{ margin: 1.5rem 0;	}
		.toc > li li		 { margin: 0.3rem 0;	}
		.toc > li li li	  { margin-left: 2rem;   }

		/* Section numbers in a column of their own */
		.toc .secno {
			float: left;
			width: 4rem;
			white-space: nowrap;
		}
		.toc > li li li li .secno { font-size: 85%; }
		.toc > li li li li li .secno { font-size: 100%; }

		.toc li {
			clear: both;
		}

		:not(li) > .toc			 { margin-left:  5rem; }
		.toc .secno				 { margin-left: -5rem; }
		.toc > li li li .secno	  { margin-left: -7rem; }
		.toc > li li li li .secno	{ margin-left: -9rem; }
		.toc > li li li li li .secno { margin-left: -11rem; }

		/* Tighten up indentation in narrow ToCs */
		@media (max-width: 30em) {
			:not(li) > .toc			 { margin-left:  4rem; }
			.toc .secno				 { margin-left: -4rem; }
			.toc > li li li			 { margin-left:  1rem; }
			.toc > li li li .secno	  { margin-left: -5rem; }
			.toc > li li li li .secno	{ margin-left: -6rem; }
			.toc > li li li li li .secno { margin-left: -7rem; }
		}
		/* Loosen it on wide screens */
		@media screen and (min-width: 78em) {
			body:not(.toc-inline) :not(li) > .toc			 { margin-left:  4rem; }
			body:not(.toc-inline) .toc .secno				 { margin-left: -4rem; }
			body:not(.toc-inline) .toc > li li li			 { margin-left:  1rem; }
			body:not(.toc-inline) .toc > li li li .secno	  { margin-left: -5rem; }
			body:not(.toc-inline) .toc > li li li li .secno	{ margin-left: -6rem; }
			body:not(.toc-inline) .toc > li li li li li .secno { margin-left: -7rem; }
	}
	/* } */

	@supports (display:grid) and (display:contents) {
		/* Use #toc over .toc to override non-@supports rules. */
		#toc {
			display: grid;
			align-content: start;
			grid-template-columns: auto 1fr;
			grid-column-gap: 1rem;
			column-gap: 1rem;
			grid-row-gap: .6rem;
			row-gap: .6rem;
		}
		#toc h2 {
			grid-column: 1 / -1;
			margin-bottom: 0;
		}
		#toc ol,
		#toc li,
		#toc a {
			display: contents;
			/* Switch <a> to subgrid when supported */
		}
		#toc span {
			margin: 0;
		}
		#toc > .toc > li > a > span {
			/* The spans of the top-level list,
			  comprising the first items of each top-level section. */
			margin-top: 1.1rem;
		}
		#toc#toc .secno { /* Ugh, need more specificity to override base.css */
			grid-column: 1;
			width: auto;
			margin-left: 0;
		}
		#toc .content {
			grid-column: 2;
			width: auto;
			margin-right: 1rem;
			border-bottom: 3px solid transparent;
			margin-bottom: -3px;
		}
		#toc .content:hover,
		#toc .content:focus {
			background: rgba(75%, 75%, 75%, .25);
			background: var(--a-hover-bg);
			border-bottom-color: #054572;
			border-bottom-color: var(--toclink-underline);
		}
		#toc li li li .content {
			margin-left: 1rem;
		}
		#toc li li li li .content {
			margin-left: 2rem;
		}
	}


/** Index *********************************************************************/

	/* Index Lists: Layout */
	ul.index	  { margin-left: 0; columns: 15em; text-indent: 1em hanging; }
	ul.index li	{ margin-left: 0; list-style: none; break-inside: avoid; }
	ul.index li li { margin-left: 1em; }
	ul.index dl	{ margin-top: 0; }
	ul.index dt	{ margin: .2em 0 .2em 20px;}
	ul.index dd	{ margin: .2em 0 .2em 40px;}
	/* Index Lists: Typography */
	ul.index ul,
	ul.index dl { font-size: smaller; }
	@media not print {
		ul.index li a + span {
			white-space: nowrap;
			color: transparent; }
		ul.index li a:hover + span,
		ul.index li a:focus + span {
			color: #707070;
			color: var(--indexinfo-text);
		}
	}

/** Index Tables *****************************************************/
	/* See also the data table styling section, which this effectively subclasses */

	table.index {
		font-size: small;
		border-collapse: collapse;
		border-spacing: 0;
		text-align: left;
		margin: 1em 0;
	}

	table.index td,
	table.index th {
		padding: 0.4em;
	}

	table.index tr:hover td:not([rowspan]),
	table.index tr:hover th:not([rowspan]) {
		color: black;
		color: var(--indextable-hover-text);
		background: #f7f8f9;
		background: var(--indextable-hover-bg);
	}

	/* The link in the first column in the property table (formerly a TD) */
	table.index th:first-child a {
		font-weight: bold;
	}

/** Outdated warning **********************************************************/

.outdated-spec {
	color: black;
	color: var(--outdatedspec-text);
	background-color: rgba(0,0,0,0.5);
	background-color: var(--outdatedspec-bg);
}

.outdated-warning {
	position: fixed;
	bottom: 50%;
	left: 0;
	right: 0;
	margin: 0 auto;
	width: 50%;
	background: maroon;
	background: var(--outdated-bg);
	color: white;
	color: var(--outdated-text);
	border-radius: 1em;
	box-shadow: 0 0 1em red;
	box-shadow: 0 0 1em var(--outdated-shadow);
	padding: 2em;
	text-align: center;
	z-index: 2;
}

.outdated-warning a {
	color: currentcolor;
	background: transparent;
}

.edited-rec-warning {
	background: darkorange;
	background: var(--editedrec-bg);
	box-shadow: 0 0 1em;
}

.outdated-warning button {
	color: var(--outdated-text);
	border-radius: 1em;
	box-shadow: 0 0 1em red;
	box-shadow: 0 0 1em var(--outdated-shadow);
	padding: 2em;
	text-align: center;
	z-index: 2;
}

.outdated-warning a {
	color: currentcolor;
	background: transparent;
}

.edited-rec-warning {
	background: darkorange;
	background: var(--editedrec-bg);
	box-shadow: 0 0 1em;
}

.outdated-warning button {
	position: absolute;
	top: 0;
	right:0;
	margin: 0;
	border: 0;
	padding: 0.25em 0.5em;
	background: transparent;
	color: white;
	color: var(--outdated-text);
	font:1em sans-serif;
	text-align:center;
}

.outdated-warning span {
	display: block;
}

.outdated-collapsed {
	bottom: 0;
	border-radius: 0;
	width: 100%;
	padding: 0;
}

/******************************************************************************/
/*                                    Print                                   */
/******************************************************************************/

	@media print {
		/* Pages have their own margins. */
		html {
			margin: 0;
		}
		/* Serif for print. */
		body {
			font-family: serif;
		}

		.outdated-warning {
			position: absolute;
			border-style: solid;
			border-color: red;
		}

		.outdated-warning input {
			display: none;
		}
	}
	@page {
		margin: 1.5cm 1.1cm;
	}



/******************************************************************************/
/*                             Overflow Control                               */
/******************************************************************************/

	.figure .caption, .sidefigure .caption, figcaption {
		/* in case figure is overlarge, limit caption to 50em */
		max-width: 50rem;
		margin-left: auto;
		margin-right: auto;
	}
	.overlarge {
		/* Magic to create good item positioning:
		  "content column" is 50ems wide at max; less on smaller screens.
		  Extra space (after ToC + content) is empty on the right.

		  1. When item < content column, centers item in column.
		  2. When content < item < available, left-aligns.
		  3. When item > available, fills available + scroll bar.
		*/
		display: grid;
		grid-template-columns: minmax(0, 50em);
	}
	.overlarge > table {
		/* limit preferred width of table */
		max-width: 50em;
		margin-left: auto;
		margin-right: auto;
	}

	@media (min-width: 55em) {
		.overlarge {
			margin-right: calc(13px + 26.5rem - 50vw);
			max-width: none;
		}
	}
	@media screen and (min-width: 78em) {
		body:not(.toc-inline) .overlarge {
			/* 30.5em body padding 50em content area */
			margin-right: calc(40em - 50vw) !important;
		}
	}
	@media screen and (min-width: 90em) {
		body:not(.toc-inline) .overlarge {
			/* 4em html margin 30.5em body padding 50em content area */
			margin-right: calc(84.5em - 100vw) !important;
		}
	}

	@media not print {
		.overlarge {
			overflow-x: auto;
			/* See Lea Verou's explanation background-attachment:
			* http://lea.verou.me/2012/04/background-attachment-local/
			*
			background: top left  / 4em 100% linear-gradient(to right,  #ffffff, rgba(255, 255, 255, 0)) local,
						top right / 4em 100% linear-gradient(to left, #ffffff, rgba(255, 255, 255, 0)) local,
						top left  / 1em 100% linear-gradient(to right,  #c3c3c5, rgba(195, 195, 197, 0)) scroll,
						top right / 1em 100% linear-gradient(to left, #c3c3c5, rgba(195, 195, 197, 0)) scroll,
						white;
			background-repeat: no-repeat;
			*/
		}
	}
</style>
<style>
    table, th, td {
      border: 1px solid black;
      border-collapse: collapse;
      vertical-align: top;
    }
    th, td {
      border-left: none;
      border-right: none;
      padding: 0px 10px;
    }
    th {
      text-align: center;
    }

    del { background: #fcc; color: #000; text-decoration: line-through; }
    ins { background: #cfc; color: #000; }
    blockquote .highlight:not(.idl) { background: initial; margin: initial; padding: 0.5em }
    blockquote ul { background: inherit; }
    blockquote code.highlight:not(.idl) { padding: initial; }
    blockquote c-[a] { color: inherit; } /* Keyword.Declaration */
    blockquote c-[b] { color: inherit; } /* Keyword.Type */
    blockquote c-[c] { color: inherit; } /* Comment */
    blockquote c-[d] { color: inherit; } /* Comment.Multiline */
    blockquote c-[e] { color: inherit; } /* Name.Attribute */
    blockquote c-[f] { color: inherit; } /* Name.Tag */
    blockquote c-[g] { color: inherit; } /* Name.Variable */
    blockquote c-[k] { color: inherit; } /* Keyword */
    blockquote c-[l] { color: inherit; } /* Literal */
    blockquote c-[m] { color: inherit; } /* Literal.Number */
    blockquote c-[n] { color: inherit; } /* Name */
    blockquote c-[o] { color: inherit; } /* Operator */
    blockquote c-[p] { color: inherit; } /* Punctuation */
    blockquote c-[s] { color: inherit; } /* Literal.String */
    blockquote c-[t] { color: inherit; } /* Literal.String.Single */
    blockquote c-[u] { color: inherit; } /* Literal.String.Double */
    blockquote c-[cp] { color: inherit; } /* Comment.Preproc */
    blockquote c-[c1] { color: inherit; } /* Comment.Single */
    blockquote c-[cs] { color: inherit; } /* Comment.Special */
    blockquote c-[kc] { color: inherit; } /* Keyword.Constant */
    blockquote c-[kn] { color: inherit; } /* Keyword.Namespace */
    blockquote c-[kp] { color: inherit; } /* Keyword.Pseudo */
    blockquote c-[kr] { color: inherit; } /* Keyword.Reserved */
    blockquote c-[ld] { color: inherit; } /* Literal.Date */
    blockquote c-[nc] { color: inherit; } /* Name.Class */
    blockquote c-[no] { color: inherit; } /* Name.Constant */
    blockquote c-[nd] { color: inherit; } /* Name.Decorator */
    blockquote c-[ni] { color: inherit; } /* Name.Entity */
    blockquote c-[ne] { color: inherit; } /* Name.Exception */
    blockquote c-[nf] { color: inherit; } /* Name.Function */
    blockquote c-[nl] { color: inherit; } /* Name.Label */
    blockquote c-[nn] { color: inherit; } /* Name.Namespace */
    blockquote c-[py] { color: inherit; } /* Name.Property */
    blockquote c-[ow] { color: inherit; } /* Operator.Word */
    blockquote c-[mb] { color: inherit; } /* Literal.Number.Bin */
    blockquote c-[mf] { color: inherit; } /* Literal.Number.Float */
    blockquote c-[mh] { color: inherit; } /* Literal.Number.Hex */
    blockquote c-[mi] { color: inherit; } /* Literal.Number.Integer */
    blockquote c-[mo] { color: inherit; } /* Literal.Number.Oct */
    blockquote c-[sb] { color: inherit; } /* Literal.String.Backtick */
    blockquote c-[sc] { color: inherit; } /* Literal.String.Char */
    blockquote c-[sd] { color: inherit; } /* Literal.String.Doc */
    blockquote c-[se] { color: inherit; } /* Literal.String.Escape */
    blockquote c-[sh] { color: inherit; } /* Literal.String.Heredoc */
    blockquote c-[si] { color: inherit; } /* Literal.String.Interpol */
    blockquote c-[sx] { color: inherit; } /* Literal.String.Other */
    blockquote c-[sr] { color: inherit; } /* Literal.String.Regex */
    blockquote c-[ss] { color: inherit; } /* Literal.String.Symbol */
    blockquote c-[vc] { color: inherit; } /* Name.Variable.Class */
    blockquote c-[vg] { color: inherit; } /* Name.Variable.Global */
    blockquote c-[vi] { color: inherit; } /* Name.Variable.Instance */
    blockquote c-[il] { color: inherit; } /* Literal.Number.Integer.Long */
  </style>
  <meta content="Bikeshed version 6270e4735, updated Tue Aug 6 12:12:30 2024 -0700" name="generator">
  <link href="https://github.com/vasama/wg21" rel="canonical">
  <link href="https://isocpp.org/favicon.ico" rel="icon">
  <meta content="5df9c1776b4a6cbc1bf05bbe909c80ddfaa4cd5b" name="revision">
  <meta content="dark light" name="color-scheme">
<style>/* Boilerplate: style-autolinks */
.css.css, .property.property, .descriptor.descriptor {
    color: var(--a-normal-text);
    font-size: inherit;
    font-family: inherit;
}
.css::before, .property::before, .descriptor::before {
    content: "‘";
}
.css::after, .property::after, .descriptor::after {
    content: "’";
}
.property, .descriptor {
    /* Don't wrap property and descriptor names */
    white-space: nowrap;
}
.type { /* CSS value <type> */
    font-style: italic;
}
pre .property::before, pre .property::after {
    content: "";
}
[data-link-type="property"]::before,
[data-link-type="propdesc"]::before,
[data-link-type="descriptor"]::before,
[data-link-type="value"]::before,
[data-link-type="function"]::before,
[data-link-type="at-rule"]::before,
[data-link-type="selector"]::before,
[data-link-type="maybe"]::before {
    content: "‘";
}
[data-link-type="property"]::after,
[data-link-type="propdesc"]::after,
[data-link-type="descriptor"]::after,
[data-link-type="value"]::after,
[data-link-type="function"]::after,
[data-link-type="at-rule"]::after,
[data-link-type="selector"]::after,
[data-link-type="maybe"]::after {
    content: "’";
}

[data-link-type].production::before,
[data-link-type].production::after,
.prod [data-link-type]::before,
.prod [data-link-type]::after {
    content: "";
}

[data-link-type=element],
[data-link-type=element-attr] {
    font-family: Menlo, Consolas, "DejaVu Sans Mono", monospace;
    font-size: .9em;
}
[data-link-type=element]::before { content: "<" }
[data-link-type=element]::after  { content: ">" }

[data-link-type=biblio] {
    white-space: pre;
}

@media (prefers-color-scheme: dark) {
    :root {
        --selflink-text: black;
        --selflink-bg: silver;
        --selflink-hover-text: white;
    }
}
</style>
<style>/* Boilerplate: style-colors */
/* Any --*-text not paired with a --*-bg is assumed to have a transparent bg */
:root {
    color-scheme: light dark;

    --text: black;
    --bg: white;

    --unofficial-watermark: url(https://www.w3.org/StyleSheets/TR/2016/logos/UD-watermark);

    --logo-bg: #1a5e9a;
    --logo-active-bg: #c00;
    --logo-text: white;

    --tocnav-normal-text: #707070;
    --tocnav-normal-bg: var(--bg);
    --tocnav-hover-text: var(--tocnav-normal-text);
    --tocnav-hover-bg: #f8f8f8;
    --tocnav-active-text: #c00;
    --tocnav-active-bg: var(--tocnav-normal-bg);

    --tocsidebar-text: var(--text);
    --tocsidebar-bg: #f7f8f9;
    --tocsidebar-shadow: rgba(0,0,0,.1);
    --tocsidebar-heading-text: hsla(203,20%,40%,.7);

    --toclink-text: var(--text);
    --toclink-underline: #3980b5;
    --toclink-visited-text: var(--toclink-text);
    --toclink-visited-underline: #054572;

    --heading-text: #005a9c;

    --hr-text: var(--text);

    --algo-border: #def;

    --del-text: red;
    --del-bg: transparent;
    --ins-text: #080;
    --ins-bg: transparent;

    --a-normal-text: #034575;
    --a-normal-underline: #bbb;
    --a-visited-text: var(--a-normal-text);
    --a-visited-underline: #707070;
    --a-hover-bg: rgba(75%, 75%, 75%, .25);
    --a-active-text: #c00;
    --a-active-underline: #c00;

    --blockquote-border: silver;
    --blockquote-bg: transparent;
    --blockquote-text: currentcolor;

    --issue-border: #e05252;
    --issue-bg: #fbe9e9;
    --issue-text: var(--text);
    --issueheading-text: #831616;

    --example-border: #e0cb52;
    --example-bg: #fcfaee;
    --example-text: var(--text);
    --exampleheading-text: #574b0f;

    --note-border: #52e052;
    --note-bg: #e9fbe9;
    --note-text: var(--text);
    --noteheading-text: hsl(120, 70%, 30%);
    --notesummary-underline: silver;

    --assertion-border: #aaa;
    --assertion-bg: #eee;
    --assertion-text: black;

    --advisement-border: orange;
    --advisement-bg: #fec;
    --advisement-text: var(--text);
    --advisementheading-text: #b35f00;

    --warning-border: red;
    --warning-bg: hsla(40,100%,50%,0.95);
    --warning-text: var(--text);

    --amendment-border: #330099;
    --amendment-bg: #F5F0FF;
    --amendment-text: var(--text);
    --amendmentheading-text: #220066;

    --def-border: #8ccbf2;
    --def-bg: #def;
    --def-text: var(--text);
    --defrow-border: #bbd7e9;

    --datacell-border: silver;

    --indexinfo-text: #707070;

    --indextable-hover-text: black;
    --indextable-hover-bg: #f7f8f9;

    --outdatedspec-bg: rgba(0, 0, 0, .5);
    --outdatedspec-text: black;
    --outdated-bg: maroon;
    --outdated-text: white;
    --outdated-shadow: red;

    --editedrec-bg: darkorange;
}

@media (prefers-color-scheme: dark) {
    :root {
        --text: #ddd;
        --bg: black;

        --unofficial-watermark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cg fill='%23100808' transform='translate(200 200) rotate(-45) translate(-200 -200)' stroke='%23100808' stroke-width='3'%3E%3Ctext x='50%25' y='220' style='font: bold 70px sans-serif; text-anchor: middle; letter-spacing: 6px;'%3EUNOFFICIAL%3C/text%3E%3Ctext x='50%25' y='305' style='font: bold 70px sans-serif; text-anchor: middle; letter-spacing: 6px;'%3EDRAFT%3C/text%3E%3C/g%3E%3C/svg%3E");

        --logo-bg: #1a5e9a;
        --logo-active-bg: #c00;
        --logo-text: white;

        --tocnav-normal-text: #999;
        --tocnav-normal-bg: var(--bg);
        --tocnav-hover-text: var(--tocnav-normal-text);
        --tocnav-hover-bg: #080808;
        --tocnav-active-text: #f44;
        --tocnav-active-bg: var(--tocnav-normal-bg);

        --tocsidebar-text: var(--text);
        --tocsidebar-bg: #080808;
        --tocsidebar-shadow: rgba(255,255,255,.1);
        --tocsidebar-heading-text: hsla(203,20%,40%,.7);

        --toclink-text: var(--text);
        --toclink-underline: #6af;
        --toclink-visited-text: var(--toclink-text);
        --toclink-visited-underline: #054572;

        --heading-text: #8af;

        --hr-text: var(--text);

        --algo-border: #456;

        --del-text: #f44;
        --del-bg: transparent;
        --ins-text: #4a4;
        --ins-bg: transparent;

        --a-normal-text: #6af;
        --a-normal-underline: #555;
        --a-visited-text: var(--a-normal-text);
        --a-visited-underline: var(--a-normal-underline);
        --a-hover-bg: rgba(25%, 25%, 25%, .2);
        --a-active-text: #f44;
        --a-active-underline: var(--a-active-text);

        --borderedblock-bg: rgba(255, 255, 255, .05);

        --blockquote-border: silver;
        --blockquote-bg: var(--borderedblock-bg);
        --blockquote-text: currentcolor;

        --issue-border: #e05252;
        --issue-bg: var(--borderedblock-bg);
        --issue-text: var(--text);
        --issueheading-text: hsl(0deg, 70%, 70%);

        --example-border: hsl(50deg, 90%, 60%);
        --example-bg: var(--borderedblock-bg);
        --example-text: var(--text);
        --exampleheading-text: hsl(50deg, 70%, 70%);

        --note-border: hsl(120deg, 100%, 35%);
        --note-bg: var(--borderedblock-bg);
        --note-text: var(--text);
        --noteheading-text: hsl(120, 70%, 70%);
        --notesummary-underline: silver;

        --assertion-border: #444;
        --assertion-bg: var(--borderedblock-bg);
        --assertion-text: var(--text);

        --advisement-border: orange;
        --advisement-bg: #222218;
        --advisement-text: var(--text);
        --advisementheading-text: #f84;

        --warning-border: red;
        --warning-bg: hsla(40,100%,20%,0.95);
        --warning-text: var(--text);

        --amendment-border: #330099;
        --amendment-bg: #080010;
        --amendment-text: var(--text);
        --amendmentheading-text: #cc00ff;

        --def-border: #8ccbf2;
        --def-bg: #080818;
        --def-text: var(--text);
        --defrow-border: #136;

        --datacell-border: silver;

        --indexinfo-text: #aaa;

        --indextable-hover-text: var(--text);
        --indextable-hover-bg: #181818;

        --outdatedspec-bg: rgba(255, 255, 255, .5);
        --outdatedspec-text: black;
        --outdated-bg: maroon;
        --outdated-text: white;
        --outdated-shadow: red;

        --editedrec-bg: darkorange;
    }
    /* In case a transparent-bg image doesn't expect to be on a dark bg,
       which is quite common in practice... */
    img { background: white; }
}
</style>
<style>/* Boilerplate: style-counters */
body {
    counter-reset: example figure issue;
}
.issue {
    counter-increment: issue;
}
.issue:not(.no-marker)::before {
    content: "Issue " counter(issue);
}

.example {
    counter-increment: example;
}
.example:not(.no-marker)::before {
    content: "Example " counter(example);
}
.invalid.example:not(.no-marker)::before,
.illegal.example:not(.no-marker)::before {
    content: "Invalid Example" counter(example);
}

figcaption {
    counter-increment: figure;
}
figcaption:not(.no-marker)::before {
    content: "Figure " counter(figure) " ";
}
</style>
<style>/* Boilerplate: style-issues */
a[href].issue-return {
    float: right;
    float: inline-end;
    color: var(--issueheading-text);
    font-weight: bold;
    text-decoration: none;
}
</style>
<style>/* Boilerplate: style-md-lists */
/* This is a weird hack for me not yet following the commonmark spec
   regarding paragraph and lists. */
[data-md] > :first-child {
    margin-top: 0;
}
[data-md] > :last-child {
    margin-bottom: 0;
}
</style>
<style>/* Boilerplate: style-selflinks */
:root {
    --selflink-text: white;
    --selflink-bg: gray;
    --selflink-hover-text: black;
}
.heading, .issue, .note, .example, li, dt {
    position: relative;
}
a.self-link {
    position: absolute;
    top: 0;
    left: calc(-1 * (3.5rem - 26px));
    width: calc(3.5rem - 26px);
    height: 2em;
    text-align: center;
    border: none;
    transition: opacity .2s;
    opacity: .5;
}
a.self-link:hover {
    opacity: 1;
}
.heading > a.self-link {
    font-size: 83%;
}
.example > a.self-link,
.note > a.self-link,
.issue > a.self-link {
    /* These blocks are overflow:auto, so positioning outside
       doesn't work. */
    left: auto;
    right: 0;
}
li > a.self-link {
    left: calc(-1 * (3.5rem - 26px) - 2em);
}
dfn > a.self-link {
    top: auto;
    left: auto;
    opacity: 0;
    width: 1.5em;
    height: 1.5em;
    background: var(--selflink-bg);
    color: var(--selflink-text);
    font-style: normal;
    transition: opacity .2s, background-color .2s, color .2s;
}
dfn:hover > a.self-link {
    opacity: 1;
}
dfn > a.self-link:hover {
    color: var(--selflink-hover-text);
}

a.self-link::before            { content: "¶"; }
.heading > a.self-link::before { content: "§"; }
dfn > a.self-link::before      { content: "#"; }
</style>
<style>/* Boilerplate: style-syntax-highlighting */
code.highlight { padding: .1em; border-radius: .3em; }
pre.highlight, pre > code.highlight { display: block; padding: 1em; margin: .5em 0; overflow: auto; border-radius: 0; }

.highlight:not(.idl) { background: rgba(0, 0, 0, .03); }
c-[a] { color: #990055 } /* Keyword.Declaration */
c-[b] { color: #990055 } /* Keyword.Type */
c-[c] { color: #708090 } /* Comment */
c-[d] { color: #708090 } /* Comment.Multiline */
c-[e] { color: #0077aa } /* Name.Attribute */
c-[f] { color: #669900 } /* Name.Tag */
c-[g] { color: #222222 } /* Name.Variable */
c-[k] { color: #990055 } /* Keyword */
c-[l] { color: #000000 } /* Literal */
c-[m] { color: #000000 } /* Literal.Number */
c-[n] { color: #0077aa } /* Name */
c-[o] { color: #999999 } /* Operator */
c-[p] { color: #999999 } /* Punctuation */
c-[s] { color: #a67f59 } /* Literal.String */
c-[t] { color: #a67f59 } /* Literal.String.Single */
c-[u] { color: #a67f59 } /* Literal.String.Double */
c-[cp] { color: #708090 } /* Comment.Preproc */
c-[c1] { color: #708090 } /* Comment.Single */
c-[cs] { color: #708090 } /* Comment.Special */
c-[kc] { color: #990055 } /* Keyword.Constant */
c-[kn] { color: #990055 } /* Keyword.Namespace */
c-[kp] { color: #990055 } /* Keyword.Pseudo */
c-[kr] { color: #990055 } /* Keyword.Reserved */
c-[ld] { color: #000000 } /* Literal.Date */
c-[nc] { color: #0077aa } /* Name.Class */
c-[no] { color: #0077aa } /* Name.Constant */
c-[nd] { color: #0077aa } /* Name.Decorator */
c-[ni] { color: #0077aa } /* Name.Entity */
c-[ne] { color: #0077aa } /* Name.Exception */
c-[nf] { color: #0077aa } /* Name.Function */
c-[nl] { color: #0077aa } /* Name.Label */
c-[nn] { color: #0077aa } /* Name.Namespace */
c-[py] { color: #0077aa } /* Name.Property */
c-[ow] { color: #999999 } /* Operator.Word */
c-[mb] { color: #000000 } /* Literal.Number.Bin */
c-[mf] { color: #000000 } /* Literal.Number.Float */
c-[mh] { color: #000000 } /* Literal.Number.Hex */
c-[mi] { color: #000000 } /* Literal.Number.Integer */
c-[mo] { color: #000000 } /* Literal.Number.Oct */
c-[sb] { color: #a67f59 } /* Literal.String.Backtick */
c-[sc] { color: #a67f59 } /* Literal.String.Char */
c-[sd] { color: #a67f59 } /* Literal.String.Doc */
c-[se] { color: #a67f59 } /* Literal.String.Escape */
c-[sh] { color: #a67f59 } /* Literal.String.Heredoc */
c-[si] { color: #a67f59 } /* Literal.String.Interpol */
c-[sx] { color: #a67f59 } /* Literal.String.Other */
c-[sr] { color: #a67f59 } /* Literal.String.Regex */
c-[ss] { color: #a67f59 } /* Literal.String.Symbol */
c-[vc] { color: #0077aa } /* Name.Variable.Class */
c-[vg] { color: #0077aa } /* Name.Variable.Global */
c-[vi] { color: #0077aa } /* Name.Variable.Instance */
c-[il] { color: #000000 } /* Literal.Number.Integer.Long */

@media (prefers-color-scheme: dark) {
    .highlight:not(.idl) { background: rgba(255, 255, 255, .05); }

    c-[a] { color: #d33682 } /* Keyword.Declaration */
    c-[b] { color: #d33682 } /* Keyword.Type */
    c-[c] { color: #2aa198 } /* Comment */
    c-[d] { color: #2aa198 } /* Comment.Multiline */
    c-[e] { color: #268bd2 } /* Name.Attribute */
    c-[f] { color: #b58900 } /* Name.Tag */
    c-[g] { color: #cb4b16 } /* Name.Variable */
    c-[k] { color: #d33682 } /* Keyword */
    c-[l] { color: #657b83 } /* Literal */
    c-[m] { color: #657b83 } /* Literal.Number */
    c-[n] { color: #268bd2 } /* Name */
    c-[o] { color: #657b83 } /* Operator */
    c-[p] { color: #657b83 } /* Punctuation */
    c-[s] { color: #6c71c4 } /* Literal.String */
    c-[t] { color: #6c71c4 } /* Literal.String.Single */
    c-[u] { color: #6c71c4 } /* Literal.String.Double */
    c-[ch] { color: #2aa198 } /* Comment.Hashbang */
    c-[cp] { color: #2aa198 } /* Comment.Preproc */
    c-[cpf] { color: #2aa198 } /* Comment.PreprocFile */
    c-[c1] { color: #2aa198 } /* Comment.Single */
    c-[cs] { color: #2aa198 } /* Comment.Special */
    c-[kc] { color: #d33682 } /* Keyword.Constant */
    c-[kn] { color: #d33682 } /* Keyword.Namespace */
    c-[kp] { color: #d33682 } /* Keyword.Pseudo */
    c-[kr] { color: #d33682 } /* Keyword.Reserved */
    c-[ld] { color: #657b83 } /* Literal.Date */
    c-[nc] { color: #268bd2 } /* Name.Class */
    c-[no] { color: #268bd2 } /* Name.Constant */
    c-[nd] { color: #268bd2 } /* Name.Decorator */
    c-[ni] { color: #268bd2 } /* Name.Entity */
    c-[ne] { color: #268bd2 } /* Name.Exception */
    c-[nf] { color: #268bd2 } /* Name.Function */
    c-[nl] { color: #268bd2 } /* Name.Label */
    c-[nn] { color: #268bd2 } /* Name.Namespace */
    c-[py] { color: #268bd2 } /* Name.Property */
    c-[ow] { color: #657b83 } /* Operator.Word */
    c-[mb] { color: #657b83 } /* Literal.Number.Bin */
    c-[mf] { color: #657b83 } /* Literal.Number.Float */
    c-[mh] { color: #657b83 } /* Literal.Number.Hex */
    c-[mi] { color: #657b83 } /* Literal.Number.Integer */
    c-[mo] { color: #657b83 } /* Literal.Number.Oct */
    c-[sa] { color: #6c71c4 } /* Literal.String.Affix */
    c-[sb] { color: #6c71c4 } /* Literal.String.Backtick */
    c-[sc] { color: #6c71c4 } /* Literal.String.Char */
    c-[dl] { color: #6c71c4 } /* Literal.String.Delimiter */
    c-[sd] { color: #6c71c4 } /* Literal.String.Doc */
    c-[se] { color: #6c71c4 } /* Literal.String.Escape */
    c-[sh] { color: #6c71c4 } /* Literal.String.Heredoc */
    c-[si] { color: #6c71c4 } /* Literal.String.Interpol */
    c-[sx] { color: #6c71c4 } /* Literal.String.Other */
    c-[sr] { color: #6c71c4 } /* Literal.String.Regex */
    c-[ss] { color: #6c71c4 } /* Literal.String.Symbol */
    c-[fm] { color: #268bd2 } /* Name.Function.Magic */
    c-[vc] { color: #cb4b16 } /* Name.Variable.Class */
    c-[vg] { color: #cb4b16 } /* Name.Variable.Global */
    c-[vi] { color: #cb4b16 } /* Name.Variable.Instance */
    c-[vm] { color: #cb4b16 } /* Name.Variable.Magic */
    c-[il] { color: #657b83 } /* Literal.Number.Integer.Long */
}
</style>
 <body class="h-entry">
  <div class="head">
   <p data-fill-with="logo"></p>
   <h1 class="p-name no-ref" id="title">P3587R0<br>Reconsider reflection access for C++26</h1>
   <h2 class="no-num no-toc no-ref heading settled" id="profile-and-date"><span class="content">Published Proposal, <time class="dt-updated" datetime="2025-01-13">2025-01-13</time></span></h2>
   <div data-fill-with="spec-metadata">
    <dl>
     <dt class="editor">Author:
     <dd class="editor p-author h-card vcard"><a class="p-name fn u-email email" href="mailto:wg21@vasama.org">Lauri Vasama</a>
     <dt>Audience:
     <dd>EWG
     <dt>Project:
     <dd>ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21
     <dt>Current draft:
     <dd><a href="https://vasama.github.io/wg21/D3587.html">vasama.github.io/wg21/D3587.html</a>
     <dt>Current draft source:
     <dd><a href="https://github.com/vasama/wg21/blob/main/D3587.bs">github.com/vasama/wg21/blob/main/D3587.bs</a>
    </dl>
   </div>
   <div data-fill-with="warning"></div>
   <hr title="Separator for header">
  </div>
  <div class="p-summary" data-fill-with="abstract">
   <h2 class="no-num no-toc no-ref heading settled" id="abstract"><span class="content">Abstract</span></h2>
   <p>Reconsider access to arbitrary private data provided by P2996.</p>
  </div>
  <nav data-fill-with="table-of-contents" id="toc">
   <h2 class="no-num no-toc no-ref" id="contents">Table of Contents</h2>
   <ol class="toc" role="directory">
    <li>
     <a href="#introduction"><span class="secno">1</span> <span class="content">Introduction</span></a>
     <ol class="toc">
      <li><a href="#introduction_metadata"><span class="secno">1.1</span> <span class="content">Metadata versus data</span></a>
      <li><a href="#introduction_data"><span class="secno">1.2</span> <span class="content">Access to object data</span></a>
     </ol>
    <li><a href="#invariants"><span class="secno">2</span> <span class="content">Invariants</span></a>
    <li>
     <a href="#motivation"><span class="secno">3</span> <span class="content">Arguments in favour</span></a>
     <ol class="toc">
      <li>
       <a href="#motivation_serialization"><span class="secno">3.1</span> <span class="content">Serialization and hashing</span></a>
       <ol class="toc">
        <li><a href="#motivation_serialization_hashing"><span class="secno">3.1.1</span> <span class="content">P2996 Hashing example</span></a>
       </ol>
      <li><a href="#motivation_existing"><span class="secno">3.2</span> <span class="content">Existing codebases</span></a>
      <li>
       <a href="#motivation_libraries"><span class="secno">3.3</span> <span class="content">External code</span></a>
       <ol class="toc">
        <li><a href="#motivation_libraries_thirdparty"><span class="secno">3.3.1</span> <span class="content">Third party libraries</span></a>
        <li><a href="#motivation_libraries_legacy"><span class="secno">3.3.2</span> <span class="content">Legacy code</span></a>
        <li><a href="#motivation_libraries_nongeneric"><span class="secno">3.3.3</span> <span class="content">Non-generic access</span></a>
       </ol>
      <li><a href="#motivation_debugging"><span class="secno">3.4</span> <span class="content">Debugging: universal formatter</span></a>
      <li>
       <a href="#motivation_broken"><span class="secno">3.5</span> <span class="content">The language is already broken</span></a>
       <ol class="toc">
        <li><a href="#motivation_broken_trick"><span class="secno">3.5.1</span> <span class="content">The pointer-to-member NTTP trick</span></a>
        <li><a href="#motivation_broken_reinterpret"><span class="secno">3.5.2</span> <span class="content">Accessing object representations</span></a>
       </ol>
     </ol>
    <li><a href="#libraries"><span class="secno">4</span> <span class="content">Library authors</span></a>
    <li><a href="#safety"><span class="secno">5</span> <span class="content">Implications for memory safety</span></a>
    <li>
     <a href="#proposal"><span class="secno">6</span> <span class="content">What to do instead?</span></a>
     <ol class="toc">
      <li>
       <a href="#proposal_options"><span class="secno">6.1</span> <span class="content">Options</span></a>
       <ol class="toc">
        <li><a href="#proposal_options_splicing"><span class="secno">6.1.1</span> <span class="content">Splicing Should Respect Access Control</span></a>
        <li><a href="#proposal_options_context"><span class="secno">6.1.2</span> <span class="content"><code class="highlight"><c- n>access_context</c-><c- o>::</c-><c- n>unchecked</c-><c- p>()</c-></code></span></a>
       </ol>
      <li>
       <a href="#proposal_useful"><span class="secno">6.2</span> <span class="content">Useful reflection without unrestricted access</span></a>
       <ol class="toc">
        <li><a href="#proposal_useful_splicing"><span class="secno">6.2.1</span> <span class="content">Access-aware splicing</span></a>
        <li><a href="#proposal_useful_decorators"><span class="secno">6.2.2</span> <span class="content">Metaclass decorators</span></a>
       </ol>
     </ol>
    <li>
     <a href="#references"><span class="secno"></span> <span class="content">References</span></a>
     <ol class="toc">
      <li><a href="#informative"><span class="secno"></span> <span class="content">Informative References</span></a>
     </ol>
   </ol>
  </nav>
  <main>
   <h2 class="heading settled" data-level="1" id="introduction"><span class="secno">1. </span><span class="content">Introduction</span><a class="self-link" href="#introduction"></a></h2>
   <p>The killer feature of reflection is automation. Automating the generation of boilerplate that could have already been written by hand. <a data-link-type="biblio" href="#biblio-p2996r8" title="Reflection for C++26">[P2996R8]</a> goes well beyond that in allowing and effectively condoning access to private members of arbitrary types. The automation of boilerplate generation is fantastically well motivated. The same cannot be said for access control bypassing.</p>
   <h3 class="heading settled" data-level="1.1" id="introduction_metadata"><span class="secno">1.1. </span><span class="content">Metadata versus data</span><a class="self-link" href="#introduction_metadata"></a></h3>
   <p>It is important to make a clear distinction between access to private <i>metadata</i> and access to private <i>data</i>. Metadata includes things such as the size and alignment of a type, the types and offsets of its member variables, and the signatures of its member functions, et cetera. The data is the values stored in the member variables of an object and the prvalues produced by taking the address of one of its member functions or static members. In a sense the metadata access already exists, only it can’t yet be automated. The programmer can manually inspect the definition a type, even an external one to which they would not otherwise have access, and produce any manner of type trait specialisation manually. But without reflection and access to private <i>metadata</i> they cannot automate it.</p>
<pre class="highlight"><c- c1>// Used for some library optimisations when it is known that the type does not</c->
<c- c1>// contain any pointer members.</c->
<c- k>template</c-><c- o>&lt;</c-><c- k>typename</c-> <c- nc>T</c-><c- o>></c->
<c- kr>inline</c-> <c- k>constexpr</c-> <c- b>bool</c-> <c- n>contains_pointers</c-> <c- o>=</c-> true<c- p>;</c->

<c- c1>// The definition of some_external_type was manually inspected to arrive at this</c->
<c- c1>// value. If you update the library version in the package manager dependencies,</c->
<c- c1>// remember to also check the definition again and update this if needed.</c->
<c- k>template</c-><c- o>&lt;></c->
<c- kr>inline</c-> <c- k>constexpr</c-> <c- b>bool</c-> <c- n>contains_pointers</c-><c- o>&lt;</c-><c- n>some_external_type</c-><c- o>></c-> <c- o>=</c-> false<c- p>;</c->
</pre>
   <p>All in all, there are a number of points in favour of access to private <i>metadata</i>:</p>
   <ol>
    <li data-md>
     <p>It has been shown that custom type traits are useful.</p>
    <li data-md>
     <p>The access is extended only to compile-time information, making it safe.</p>
    <li data-md>
     <p>Users can already implement custom type traits manually in a fragile way.</p>
   </ol>
   <p>For these reasons we are not opposed to access to arbitrary private <i>metadata</i>.</p>
   <h3 class="heading settled" data-level="1.2" id="introduction_data"><span class="secno">1.2. </span><span class="content">Access to object data</span><a class="self-link" href="#introduction_data"></a></h3>
   <p>Accessing private data is undoubtedly useful and we are not opposed to that. However there is a stark difference between accessing private data with permission and breaking through access controls to access <i>arbitrary</i> private data, e.g. that of <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-></code>.</p>
   <p>Access to private data is often presented as something necessary for useful reflection, but that is not precisely correct. Access to certain private data is indeed necessary, but access to <i>arbitrary</i> private data is not. It is entirely possible to construct protocols for types to opt into automatic hashing or serialization. The simplest way to do that is by befriending:</p>
<pre class="highlight"><c- k>class</c-> <c- nc>my_class</c-> <c- p>{</c->
    <c- b>int</c-> <c- n>private_data_to_serialize</c-><c- p>;</c->

    <c- c1>// Give access to the serialization library:</c->
    <c- k>friend</c-> <c- n>serialization_lib</c-><c- o>::</c-><c- n>access</c-><c- p>;</c->
<c- p>}</c->
</pre>
   <p>It’s important to understand that automatic serialization of arbitrary types without guidance specific to that type (e.g. in the form of annotations) is not possible.</p>
   <h2 class="heading settled" data-level="2" id="invariants"><span class="secno">2. </span><span class="content">Invariants</span><a class="self-link" href="#invariants"></a></h2>
   <p>In general, private access control implies invariants. Access control is the mechanism provided by the language for <i>maintaining</i> those invariants. Only code within the class or within befriended entities may use private members, because only such code can be trusted to understand that invariants applied to those members.</p>
   <p>It is not possible for a generic serialization or hashing or other algorithm to divine the arbitrary invariants applied to a private member. This includes more obvious invariants such as restrictions on the values of data members, but also more insidious ones such as being restricted to be accessed only by a specific thread.</p>
   <p>When a member is being mutated by another thread, even reading it just for debug purposes is dangerous. Even seemingly simple types such as <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string_view</c-></code> or pointers to members, being larger than what most CPU architectures can copy in a single operation, can easily result in invalid values in the presence of data races. It’s easy to see how a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string_view</c-></code> with its address member read before a write and its size member after results in an unusable value that at best points to garbage and at worst to a memory-mapped register producing side-effects when read.</p>
   <p>It might seem attractive to constrain algorithms based on the types of members, for example by rejecting any types containing pointers. This is entirely insufficient to alleviate the problem but even if it weren’t, pointers can hide in seemingly safe types such as integers. And this is not as contrived as it might seem at first glance. Types such as tagged pointers are increasingly common in low-level or high-performance scenarios where it is important to save space. There is even a proposal to add language support for tagged pointers: <a data-link-type="biblio" href="#biblio-p3125r0" title="Pointer tagging">[P3125R0]</a>.</p>
   <p>The conclusion is that <a data-link-type="biblio" href="#biblio-p2996r8" title="Reflection for C++26">[P2996R8]</a> proposes to break a very old and fundamental assumption of the language: that private members are safe from outside access. Not just safe from mutation but safe from data races.</p>
   <h2 class="heading settled" data-level="3" id="motivation"><span class="secno">3. </span><span class="content">Arguments in favour</span><a class="self-link" href="#motivation"></a></h2>
   <p>Various pieces of motivation have been provided in favour of bypassing access control. In this section we go over all pieces of motivation known to us.</p>
   <h3 class="heading settled" data-level="3.1" id="motivation_serialization"><span class="secno">3.1. </span><span class="content">Serialization and hashing</span><a class="self-link" href="#motivation_serialization"></a></h3>
   <p>Serialization and hashing have long been two poster child examples of reflection use cases. Both often involve repetitive listing of non-static data members. Whenever a new member is added, the developer must remember to add it to the list of things to hash or to serialize. This represents a perfect opportunity for automation using reflection. As explained in {#invariants} however, it is not possible for generic code to maintain arbitry invariants. Real serialization involving private members inevitably requires guidance. In many languages this comes in the form of annotations such as those proposed in <a data-link-type="biblio" href="#biblio-p3394r0" title="Annotations for Reflection">[P3394R0]</a>. It cannot be said that opting in is too inconvenient when in practice annotations or another mechanism to provide guidance is already necessary:</p>
<pre class="highlight"><c- k>class</c-> <c- nc>my_class</c-> <c- p>{</c->
    <c- b>int</c-> <c- n>private_data_to_serialize</c-><c- p>;</c->

    <c- p>[[</c-><c- o>=</c-><c- n>serializer_lib</c-><c- o>::</c-><c- n>transient</c-><c- p>]]</c->
    <c- b>void</c-><c- o>*</c-> <c- n>transient_pointer</c-><c- p>;</c->

    <c- k>friend</c-> <c- n>serializer_lib</c-><c- o>::</c-><c- n>access</c-><c- p>;</c->
<c- p>};</c->
</pre>
   <h4 class="heading settled" data-level="3.1.1" id="motivation_serialization_hashing"><span class="secno">3.1.1. </span><span class="content">P2996 Hashing example</span><a class="self-link" href="#motivation_serialization_hashing"></a></h4>
   <p><a data-link-type="biblio" href="#biblio-p2996r8" title="Reflection for C++26">[P2996R8]</a> shows the following hashing use case, based on the <a data-link-type="biblio" href="#biblio-n3980" title="Types don&apos;t know #">[N3980]</a> API:</p>
<pre class="highlight"><c- k>template</c-> <c- o>&lt;</c-><c- k>typename</c-> <c- nc>H</c-><c- p>,</c-> <c- k>typename</c-> <c- nc>T</c-><c- o>></c-> <c- k>requires</c-> <c- n>std</c-><c- o>::</c-><c- n>is_standard_layout_v</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c->
<c- b>void</c-> <c- n>hash_append</c-><c- p>(</c-><c- n>H</c-><c- o>&amp;</c-> <c- n>algo</c-><c- p>,</c-> <c- n>T</c-> <c- k>const</c-><c- o>&amp;</c-> <c- n>t</c-><c- p>)</c-> <c- p>{</c->
  <c- k>template</c-> <c- k>for</c-> <c- p>(</c-><c- k>constexpr</c-> <c- k>auto</c-> <c- n>mem</c-> <c- o>:</c-> <c- n>nonstatic_data_members_of</c-><c- p>(</c-><c- o>^</c-><c- n>T</c-><c- p>))</c-> <c- p>{</c->
      <c- n>hash_append</c-><c- p>(</c-><c- n>algo</c-><c- p>,</c-> <c- n>t</c-><c- p>.[</c-><c- o>:</c-><c- n>mem</c-><c- o>:</c-><c- p>]);</c->
  <c- p>}</c->
<c- p>}</c->
</pre>
   <p>After implementing a simple hash algorithm based on <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>hash</c-></code> for types where available, we can see it in action:</p>
<pre class="highlight"><c- n>my</c-><c- o>::</c-><c- n>hash_algo</c-> <c- n>algo</c-> <c- o>=</c-> <c- p>{};</c->
<c- n>hash_append</c-><c- p>(</c-><c- n>algo</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>expected</c-><c- o>&lt;</c-><c- b>int</c-><c- p>,</c-> <c- k>const</c-> <c- b>char</c-><c- o>*></c-><c- p>(</c-><c- mi>42</c-><c- p>));</c->
<c- n>std</c-><c- o>::</c-><c- n>print</c-><c- p>(</c-><c- s>"hash = {}</c-><c- se>\n</c-><c- s>"</c-><c- p>,</c-> <c- n>algo</c-><c- p>.</c-><c- n>accumulator</c-><c- p>);</c->
</pre>
   <p>On Compiler Explorer: <a href="https://godbolt.org/z/Txjfc5777">Clang</a></p>
   <p>Here <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>expected</c-></code>, which is equality comparable but not hashable out of the box, is made hashable through the use of the universal hashing algorithm. The problem with this is that <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>expected</c-></code> contains a union and it is not possible for a generic algorithm to detect the active member of that union. Naively, the algorithm presented in <a data-link-type="biblio" href="#biblio-p2996r8" title="Reflection for C++26">[P2996R8]</a> ignores the presence of the union and proceeds to visit each non-static data member regardless. Not only does this result in undefined behaviour, but the algorithm didn’t even manage to produce a valid hash:</p>
<pre class="highlight"><c- n>std</c-><c- o>::</c-><c- n>expected</c-> <c- n>e1</c-> <c- o>=</c-> <c- p>...;</c->
<c- n>std</c-><c- o>::</c-><c- n>expected</c-> <c- n>e2</c-> <c- o>=</c-> <c- p>...;</c->

<c- b>size_t</c-> <c- n>h1</c-> <c- o>=</c-> <c- n>hash</c-><c- p>(</c-><c- n>e1</c-><c- p>);</c->
<c- b>size_t</c-> <c- n>h2</c-> <c- o>=</c-> <c- n>hash</c-><c- p>(</c-><c- n>e2</c-><c- p>);</c->

<c- n>std</c-><c- o>::</c-><c- n>print</c-><c- p>(</c-><c- s>"e1 == e2 : {}</c-><c- se>\n</c-><c- s>"</c-><c- p>,</c-> <c- p>(</c-><c- n>e1</c-> <c- o>==</c-> <c- n>e2</c-><c- p>)</c-> <c- o>?</c-> <c- s>"true"</c-> <c- o>:</c-> <c- s>"false"</c-><c- p>);</c->
<c- n>std</c-><c- o>::</c-><c- n>print</c-><c- p>(</c-><c- s>"h1 == h2 : {}</c-><c- se>\n</c-><c- s>"</c-><c- p>,</c-> <c- p>(</c-><c- n>h1</c-> <c- o>==</c-> <c- n>h2</c-><c- p>)</c-> <c- o>?</c-> <c- s>"true"</c-> <c- o>:</c-> <c- s>"false"</c-><c- p>);</c->
</pre>
   <p>Execution of this program resulted in the following output:</p>
<pre class="highlight"><c- n>e1</c-> <c- o>==</c-> <c- n>e2</c-> <c- o>:</c-> true
<c- n>h1</c-> <c- o>==</c-> <c- n>h2</c-> <c- o>:</c-> false
</pre>
   <p>On Compiler Explorer: <a href="https://godbolt.org/z/7ca9cjbqK">Clang</a>, Screenshot on <a href="https://vasama.github.io/wg21/images/universal-hash-expected.png">GitHub</a></p>
   <p>This is not to say that an expert C++ programmer could not come up with rules to constrain the set of types to which such an algorithm can be applied, e.g. by rejecting any type containing unions, among others. Although it must be pointed out that no amount of expertise can protect the algorithm from data races arising due to usually safe mutation of private data by another thread. This example does showcase how the sort of implementations that might be created by regular developers (e.g. by copying code from a WG21 paper) present a massive footgun.</p>
   <p>After making the generic hashing algorithm more and more conservative, one finds that the safe end result is an algorithm constrained to work only on the set of class types containing no private bases or non-static data members, thus obviating the proposed motivation for accessing arbitrary private <i>data</i>. Note that at the same time, it is useful to determine whether a type has any private members in order to reject it, once again reinforcing the need for access to private <i>metadata</i>.</p>
   <h3 class="heading settled" data-level="3.2" id="motivation_existing"><span class="secno">3.2. </span><span class="content">Existing codebases</span><a class="self-link" href="#motivation_existing"></a></h3>
   <p>It has been argued that users with large codebases may wish to apply reflection to a large number of existing types automatically, say for the purposes of serialization, and that having to provide access to the reflecting code (e.g. by befriending) is <i>cumbersome</i>. There are a few flaws with this reasoning:</p>
   <ol>
    <li data-md>
     <p>It is not possible to correctly serialize arbitrary types with invariants.</p>
    <li data-md>
     <p>Any such existing code very likely already includes manually written equivalents of the code soon to be generated through reflection. In the switch to automatic serialization, that code likely has to be removed.</p>
   </ol>
   <p>For these reasons, any such types would in any case have to undergo manual audits and modifications before a generic reflection based serializer can be applied. It is hard to imagine a scenario where reflection could be succesfully applied to a large number of existing types with invariants for any non-trivial use case and without any changes to the types in question.</p>
   <h3 class="heading settled" data-level="3.3" id="motivation_libraries"><span class="secno">3.3. </span><span class="content">External code</span><a class="self-link" href="#motivation_libraries"></a></h3>
   <h4 class="heading settled" data-level="3.3.1" id="motivation_libraries_thirdparty"><span class="secno">3.3.1. </span><span class="content">Third party libraries</span><a class="self-link" href="#motivation_libraries_thirdparty"></a></h4>
   <p>Some users have expressed a desire to access the internals of third-party libraries in order to implement features not provided in the public API of the library. This may involve reading or writing private member variables or invoking private member functions. Proponents will argue that cooperating with library authors and upstreaming changes is difficult; it requires getting along with people. Forking and maintaining customized versions of libraries for private use is considered to be too much effort.</p>
   <p>The standard library itself is often used as an example of this. Despite the introduction of <code class="highlight"><c- n>resize_for_overwrite</c-></code>, some users would like to resize standard library containers without initializing their elements. There are good reasons for wanting to do this - particularly asynchronous initialization - but it is a problem to be solved in the committee; a social problem. (Or alternatively as a non-standard extension.)</p>
   <h4 class="heading settled" data-level="3.3.2" id="motivation_libraries_legacy"><span class="secno">3.3.2. </span><span class="content">Legacy code</span><a class="self-link" href="#motivation_libraries_legacy"></a></h4>
   <p>Some users depend on libraries that simply cannot ever be changed, for whatever political reasons. This could be considered yet another social problem, but for the sake of the argument we shall consider it a business requirement. This begs the question of why whatever entity is blocking changes to this code would instead allow accessing its internals and potentially violating its invariants. Perhaps it is thought that an access bypassing solution applied from the outside might fly under the radar if not too loudly advertised.</p>
   <p>Using reflection to automate certain tasks such as serialization has the benefit of making the code more resistant to changes. It is not possible to forget to add a member variable to the list of those to be serialized when it is done automatically. However, when the premise is that a library can never change, there is no such benefit gained.</p>
   <h4 class="heading settled" data-level="3.3.3" id="motivation_libraries_nongeneric"><span class="secno">3.3.3. </span><span class="content">Non-generic access</span><a class="self-link" href="#motivation_libraries_nongeneric"></a></h4>
   <p>These examples provide some motivation for breaking through controls to access <i>specific</i> members which invariants and usage is <i>understood</i> by the developer. Using the facilities proposed in <a data-link-type="biblio" href="#biblio-p2996r8" title="Reflection for C++26">[P2996R8]</a>, this would likely be achieved using something like the following accessor:</p>
<pre class="highlight"><c- k>consteval</c-> <c- n>std</c-><c- o>::</c-><c- n>meta</c-><c- o>::</c-><c- n>info</c-> <c- nf>get_nsdm_helper</c-><c- p>(</c->
  <c- n>std</c-><c- o>::</c-><c- n>meta</c-><c- o>::</c-><c- n>info</c-> <c- n>type</c-><c- p>,</c->
  <c- n>std</c-><c- o>::</c-><c- n>string_view</c-> <c- n>name</c-><c- p>)</c-> <c- p>{</c->
  <c- k>for</c-> <c- p>(</c-><c- k>auto</c-> <c- n>nsdm</c-> <c- o>:</c-> <c- n>nonstatic_data_members_of</c-><c- p>(</c-><c- n>type</c-><c- p>))</c-> <c- p>{</c->
    <c- k>if</c-> <c- p>(</c-><c- n>has_identifier</c-><c- p>(</c-><c- n>nsdm</c-><c- p>)</c-> <c- o>&amp;&amp;</c-> <c- n>identifier_of</c-><c- p>(</c-><c- n>nsdm</c-><c- p>)</c-> <c- o>==</c-> <c- n>name</c-><c- p>)</c->
      <c- k>return</c-> <c- n>nsdm</c-><c- p>;</c->
  <c- p>}</c->
  <c- n>std</c-><c- o>::</c-><c- n>unreachable</c-><c- p>();</c->
<c- p>}</c->

<c- k>template</c-><c- o>&lt;</c-><c- n>static_string</c-> <c- n>Name</c-><c- p>,</c-> <c- k>typename</c-> <c- nc>T</c-><c- o>></c->
<c- k>decltype</c-><c- p>(</c-><c- k>auto</c-><c- p>)</c-> <c- n>get_nsdm</c-><c- p>(</c-><c- n>T</c-><c- o>&amp;&amp;</c-> <c- n>object</c-><c- p>)</c-> <c- p>{</c->
  <c- k>return</c-> <c- n>std</c-><c- o>::</c-><c- n>forward_like</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-><c- p>(</c-><c- n>object</c-><c- p>.[</c-><c- o>:</c-><c- n>get_nsdm_helper</c-><c- p>(</c-><c- o>^^</c-><c- n>T</c-><c- p>,</c-> <c- n>Name</c-><c- p>)</c-><c- o>:</c-><c- p>])</c->
<c- p>}</c->

<c- k>template</c-><c- o>&lt;</c-><c- k>typename</c-> <c- nc>T</c-><c- o>></c->
<c- b>void</c-> <c- n>resize_vector_uninitialized</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>vector</c-><c- o>&lt;</c-><c- n>T</c-><c- o>>&amp;</c-> <c- n>vec</c-><c- p>,</c-> <c- b>size_t</c-> <c- n>new_size</c-><c- p>)</c-> <c- p>{</c->
    <c- n>vec</c-><c- p>.</c-><c- n>reserve</c-><c- p>(</c-><c- n>new_size</c-><c- p>);</c->

<c- cp>#if defined(_MSVC_STL_VERSION)</c->
    <c- k>auto</c-><c- o>&amp;</c-> <c- n>pointers</c-> <c- o>=</c-> <c- n>get_nsdm</c-><c- o>&lt;</c-><c- s>"_Mypair"</c-><c- o>></c-><c- p>(</c-><c- n>object</c-><c- p>).</c-><c- n>_Myval2</c-><c- p>;</c->
    <c- n>pointers</c-><c- p>.</c-><c- n>_Mylast</c-> <c- o>=</c-> <c- n>pointers</c-><c- p>.</c-><c- n>_Myfirst</c-> <c- o>+</c-> <c- n>new_size</c-><c- p>;</c->
<c- cp>#elif ...</c->
    <c- c1>// Handle other implementations...</c->
<c- cp>#endif</c->
<c- p>}</c->
</pre>
   <p>This seems to have very little to do with <i>reflection itself</i>, which is only used here as a tool for breaking through access controls. There is no automation of any task or decisions made based on reflecting the properties of a type. In fact this sort of use case would be much better served by a different kind of facility providing direct
access to private members:</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>typename</c-> <c- nc>T</c-><c- o>></c->
<c- b>void</c-> <c- n>resize_vector_uninitialized</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>vector</c-><c- o>&lt;</c-><c- n>T</c-><c- o>>&amp;</c-> <c- n>vec</c-><c- p>,</c-> <c- b>size_t</c-> <c- n>new_size</c-><c- p>)</c-> <c- p>{</c->
    <c- n>vec</c-><c- p>.</c-><c- n>reserve</c-><c- p>(</c-><c- n>new_size</c-><c- p>);</c->

<c- cp>#if defined(_MSVC_STL_VERSION)</c->
    <c- k>auto</c-><c- o>&amp;</c-> <c- n>pointers</c-> <c- o>=</c-> <c- n>object</c-><c- p>.</c-><c- k>private</c-> <c- n>_Myval2</c-><c- p>;</c->
    <c- n>pointers</c-><c- p>.</c-><c- n>_Mylast</c-> <c- o>=</c-> <c- n>pointers</c-><c- p>.</c-><c- n>_Myfirst</c-> <c- o>+</c-> <c- n>new_size</c-><c- p>;</c->
<c- cp>#elif ...</c->
    <c- c1>// Handle other implementations...</c->
<c- cp>#endif</c->
<c- p>}</c->
</pre>
   <p>Here the strawman syntax <code class="highlight"><c- n>x</c-><c- p>.</c-><c- k>private</c-> <c- n>y</c-></code> is used for accessing the private member <code class="highlight"><c- n>y</c-></code> of object <code class="highlight"><c- n>x</c-></code>.</p>
   <p>These sorts of examples have been presented in the past as rebuttals to questions about the necessity for access to arbitrary private data, but to our knowledge have not been presented in any papers targeting EWG. Perhaps these are valuable use cases, but in that case it should be brought to the committee in a separate paper and considered on its own merits.</p>
   <p>Note that, hypothetically, if the above strawman syntax were added to the language, it would be very easy to search for and it would automatically benefit from reflection, as other existing language constructs do: <code class="highlight"><c- n>x</c-><c- p>.</c-><c- k>private</c-> <c- p>[</c-><c- o>:</c-> <c- n>info</c-> <c- o>:</c-><c- p>]</c-></code>. Note also that this paper does not propose the addition of any new syntax.</p>
   <h3 class="heading settled" data-level="3.4" id="motivation_debugging"><span class="secno">3.4. </span><span class="content">Debugging: universal formatter</span><a class="self-link" href="#motivation_debugging"></a></h3>
   <p><a data-link-type="biblio" href="#biblio-p2996r8" title="Reflection for C++26">[P2996R8]</a> provides only a single motivating example for debugging: the universal formatter. While dumping class fields purely for debugging purposes is undoubtedly a useful thing to do, there are a few important things to note:</p>
   <ol>
    <li data-md>
     <p>This is purely a debugging facility. It is not possible to correctly deserialize the output for arbitrary types.</p>
    <li data-md>
     <p>When accessing the private data members of arbitrary types, normally safe concurrent modifications internal to the class cannot be ruled out. In this scenario the dumping code may very easily invoke undefined behaviour through data races. These races may be benign if the dumping code is careful to recurse through subobjects and only print out scalars. The worst case then would be sanitizer warnings or tearing depending on the platform and data type. <br><br> However, if the dumping code does anything more complicated, such as printing some values using <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>format</c-></code>, those data races will very quickly turn malignant. Dereferencing a torn or stale pointer will clearly result in unpredictable consequences. The best case scenario is a crash or reading garbage. The worst might be an observable side effect if a read through an invalid pointer happened to hit a memory mapped register or a guard page. The universal formatter example shown in <a data-link-type="biblio" href="#biblio-p2996r8" title="Reflection for C++26">[P2996R8]</a> falls into this trap.</p>
   </ol>
   <h3 class="heading settled" data-level="3.5" id="motivation_broken"><span class="secno">3.5. </span><span class="content">The language is already broken</span><a class="self-link" href="#motivation_broken"></a></h3>
   <h4 class="heading settled" data-level="3.5.1" id="motivation_broken_trick"><span class="secno">3.5.1. </span><span class="content">The pointer-to-member NTTP trick</span><a class="self-link" href="#motivation_broken_trick"></a></h4>
   <p>Proponents of access bypassing argue that because the language already allows bypassing access controls through an obscure and convoluted trick involving explicit class template specializations with a pointer-to-member NTTP, there is no further harm in allowing easy access to all privates:</p>
<pre class="highlight"><c- k>class</c-> <c- nc>Victim</c-> <c- p>{</c-> <c- b>int</c-> <c- n>secret</c-> <c- o>=</c-> <c- mi>42</c-><c- p>;</c-> <c- p>};</c->

<c- b>int</c-><c- o>&amp;</c-> <c- nf>rob_secret</c-><c- p>(</c-><c- n>Victim</c-><c- o>&amp;</c-> <c- n>v</c-><c- p>);</c->

<c- k>template</c-> <c- o>&lt;</c-><c- b>int</c-> <c- n>Victim</c-><c- o>::*</c-> <c- n>M</c-><c- o>></c->
<c- k>struct</c-> <c- nc>Robber</c-> <c- p>{</c->
    <c- k>friend</c-> <c- b>int</c-><c- o>&amp;</c-> <c- nf>rob_secret</c-><c- p>(</c-><c- n>Victim</c-><c- o>&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
        <c- k>return</c-> <c- n>v</c-><c- p>.</c-><c- o>*</c-><c- n>M</c-><c- p>;</c->
    <c- p>}</c->
<c- p>};</c->

<c- k>template</c-> <c- k>struct</c-> <c- nc>Robber</c-><c- o>&lt;&amp;</c-><c- n>Victim</c-><c- o>::</c-><c- n>secret</c-><c- o>></c-><c- p>;</c->

<c- b>void</c-> <c- nf>f</c-><c- p>(</c-><c- n>Victim</c-><c- o>&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
    <c- n>std</c-><c- o>::</c-><c- n>print</c-><c- p>(</c-><c- s>"v.secret = {}</c-><c- se>\n</c-><c- s>"</c-><c- p>,</c-> <c- n>rob_secret</c-><c- p>(</c-><c- n>victim</c-><c- p>));</c->
<c- p>}</c->
</pre>
   <p>See it on <a href="https://godbolt.org/z/YTPoe3eeM">Compiler Explorer</a></p>
   <ol>
    <li data-md>
     <p>This trick is extremely obscure and not widely used.</p>
    <li data-md>
     <p>Applying it requires defining individual explicit template instantiations for each member being accessed.</p>
    <li data-md>
     <p>It comes with a host of platform specific limitations. See here: <a href="https://github.com/schaumb/access_private_20">GitHub</a></p>
   </ol>
   <p>Proponents also claim that without first-class access control bypassing, users will simply apply reflection to automate access using this trick. While some level of automation might be achievable using token injection, there is certainly no way of automating this using the facilities proposed in <a data-link-type="biblio" href="#biblio-p2996r8" title="Reflection for C++26">[P2996R8]</a>.</p>
   <h4 class="heading settled" data-level="3.5.2" id="motivation_broken_reinterpret"><span class="secno">3.5.2. </span><span class="content">Accessing object representations</span><a class="self-link" href="#motivation_broken_reinterpret"></a></h4>
   <p>Similarly it is argued that given access to arbitrary private metadata, including non-static data member offsets, it is also possible to access the data using <code class="highlight"><c- k>reinterpret_cast</c-></code> and pointer arithmetic, and that it is better to allow direct access than to risk users implementing the same thing in unscrupulous ways:</p>
<pre class="highlight"><c- k>class</c-> <c- nc>privates</c-> <c- p>{</c->
    <c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>s</c-><c- p>;</c->

<c- k>public</c-><c- o>:</c->
    <c- n>privates</c-><c- p>()</c->
        <c- o>:</c-> <c- n>s</c-><c- p>(</c-><c- s>"secret"</c-><c- p>)</c-> <c- p>{}</c->
<c- p>};</c->

<c- b>void</c-> <c- nf>f</c-><c- p>(</c-><c- n>privates</c-><c- o>&amp;</c-> <c- n>p</c-><c- p>)</c-> <c- p>{</c->
    <c- k>template</c-> <c- k>for</c-> <c- p>(</c-><c- k>constexpr</c-> <c- k>auto</c-> <c- n>member</c-> <c- o>:</c-> <c- n>nonstatic_data_members_of</c-><c- p>(</c-><c- o>^^</c-><c- n>privates</c-><c- p>))</c-> <c- p>{</c->
        <c- b>void</c-><c- o>*</c-> <c- n>m</c-> <c- o>=</c-> <c- k>reinterpret_cast</c-><c- o>&lt;</c-><c- b>unsigned</c-> <c- b>char</c-><c- o>*></c-><c- p>(</c-><c- o>&amp;</c-><c- n>p</c-><c- p>)</c-> <c- o>+</c-> <c- n>offset_of</c-><c- p>(</c-><c- n>member</c-><c- p>).</c-><c- n>bytes</c-><c- p>;</c->
        <c- n>std</c-><c- o>::</c-><c- n>print</c-><c- p>(</c-><c- s>"{}"</c-><c- p>,</c-> <c- o>*</c-><c- k>static_cast</c-><c- o>&lt;</c-><c- k>typename</c-> <c- p>[</c-><c- o>:</c-> <c- n>type_of</c-><c- p>(</c-><c- n>member</c-><c- p>)</c-> <c- o>:</c-><c- p>]</c-><c- o>*></c-><c- p>(</c-><c- n>m</c-><c- p>));</c->
    <c- p>}</c->
<c- p>}</c->
</pre>
   <p>On Compiler Explorer: <a href="https://godbolt.org/z/5jjErE9z3">Clang</a></p>
   <p>This code has undefined behaviour and thanks to the efforts of educators in our community, more than ever, our users understand that. This sort of code sticks out like a sore thumb in code review. In any case, much like the member pointer specialisation trick, this "solution" only provides access to a subset of private members, while still leaving others protected. This is to say that the proposed changes in <a data-link-type="biblio" href="#biblio-p2996r8" title="Reflection for C++26">[P2996R8]</a> do inevitably open up access
to previously inaccessible members.</p>
   <h2 class="heading settled" data-level="4" id="libraries"><span class="secno">4. </span><span class="content">Library authors</span><a class="self-link" href="#libraries"></a></h2>
   <p>For library authors, access to arbitrary private data represents a colossal increase in API surface area. With the condonement by the language to access private members, any visible private member of a class is implicitly made part of a library API. The ABI surface areas of libraries may also be increased, though not to the same extent because many private members - particularly private non-static data members - already contribute to library ABIs.</p>
   <p>Even when library authors choose to ignore API breaks caused only by private members, the inevitable complaints about them will create an undue burden on the maintainer. To combat these issues, we may see increased use of the PIMPL pattern (<a href="https://en.cppreference.com/w/cpp/language/pimpl">cppreference.com</a>) as a defensive measure, potentially introducing runtime inefficiency and preventing optimisations. Additionally we may see calls for a way to once more prevent some members from being reflected, e.g using a new access control syntax (strawman syntax):</p>
<pre class="highlight"><c- k>class</c-> <c- nc>future_class</c-> <c- p>{</c->
true <c- k>private</c-><c- o>:</c->
  <c- b>int</c-> <c- n>non_reflectable_member</c-><c- p>;</c->
<c- p>};</c->
</pre>
   <h2 class="heading settled" data-level="5" id="safety"><span class="secno">5. </span><span class="content">Implications for memory safety</span><a class="self-link" href="#safety"></a></h2>
   <p><a data-link-type="biblio" href="#biblio-p3390r0" title="Safe C++">[P3390R0]</a> proposes a set of extensions for authoring provably memory-safe code in C++. In part this relies on the ability to author safe class types (e.g. a safe alternative to <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-></code>) containing unsafe code in their implementations. In this scenario the uncheckable invariants applied to private members become even more significant than before. Unregulated outside access to the member data of such a type cannot be allowed in any safe context. This requirement encompasses members of a fundamentally safe type such as integer types as well as read-only access due to the potential for data races.</p>
   <p>Even in the absence of provable memory safety, access to arbitrary private member data must be considered an unsafe operation. <a data-link-type="biblio" href="#biblio-p3081r0" title="Core safety Profiles: Specification, adoptability, and impact">[P3081R0]</a> proposes a set of safety profiles to mitigate existing safety problems. If C++26 ships access breaking reflection, should it also ship an <code class="highlight"><c- n>invariant_safety</c-></code> profile rejecting access to private data?</p>
   <h2 class="heading settled" data-level="6" id="proposal"><span class="secno">6. </span><span class="content">What to do instead?</span><a class="self-link" href="#proposal"></a></h2>
   <p>The crux of this proposal is that C++26 should not provide access to <i>arbitrary private data</i>. Access to arbitrary private <i>metadata</i> and access to private data when given permission are both acceptable and the latter is extremely important for useful reflection.</p>
   <p>This paper proposes to ship a minimum viable reflection facility in C++26 without the unsafe and unnecessary access to arbitrary private data. One that provides ways to automate the tasks already performed by developers without fundamentally changing the language.</p>
   <p>If C++26 ships with access breaking, it is unlikely that the committee can ever remove it. <br> If C++26 ships without access breaking, it is trivial to introduce it in C++29 after further consideration.</p>
   <p>Even without access breaking <a data-link-type="biblio" href="#biblio-p2996r8" title="Reflection for C++26">[P2996R8]</a> will make a fantastic addition C++ and undoubtedly bring great advancements in developer productivity.</p>
   <h3 class="heading settled" data-level="6.1" id="proposal_options"><span class="secno">6.1. </span><span class="content">Options</span><a class="self-link" href="#proposal_options"></a></h3>
   <h4 class="heading settled" data-level="6.1.1" id="proposal_options_splicing"><span class="secno">6.1.1. </span><span class="content">Splicing Should Respect Access Control</span><a class="self-link" href="#proposal_options_splicing"></a></h4>
   <p><a data-link-type="biblio" href="#biblio-p3473r0" title="Splicing Should Respect Access Control">[P3473R0]</a> proposes applying usual access checks to splice expressions:</p>
<pre class="highlight"><c- k>class</c-> <c- nc>S</c-> <c- p>{</c->
  <c- b>int</c-> <c- n>priv</c-><c- p>;</c->
<c- k>public</c-><c- o>:</c->
  <c- n>S</c-><c- p>()</c-> <c- o>:</c-> <c- n>priv</c-><c- p>(</c-><c- mi>0</c-><c- p>)</c-> <c- p>{}</c->
<c- p>};</c->

<c- k>consteval</c-> <c- k>auto</c-> <c- n>get_member</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>meta</c-><c- o>::</c-><c- n>info</c-> <c- n>info</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>string_view</c-> <c- n>name</c-><c- p>)</c-> <c- p>{</c->
  <c- k>for</c-> <c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>meta</c-><c- o>::</c-><c- n>info</c-> <c- n>field</c-> <c- o>:</c-> <c- n>nonstatic_data_members_of</c-><c- p>(</c-><c- n>info</c-><c- p>))</c-> <c- p>{</c->
    <c- k>if</c-> <c- p>(</c-><c- n>has_identifier</c-><c- p>(</c-><c- n>field</c-><c- p>)</c-> <c- o>&amp;&amp;</c-> <c- n>identifier_of</c-><c- p>(</c-><c- n>field</c-><c- p>)</c-> <c- o>==</c-> <c- n>name</c-><c- p>)</c->
      <c- k>return</c-> <c- n>field</c-><c- p>;</c->
  <c- p>}</c->
  <c- n>std</c-><c- o>::</c-><c- n>unreachable</c-><c- p>();</c->
<c- p>}</c->

<c- b>int</c-> <c- n>main</c-><c- p>()</c-> <c- p>{</c->
    <c- n>S</c-> <c- n>s</c-><c- p>;</c->

    <c- c1>// Accessing private metadata is still fine:</c->
    <c- k>constexpr</c-> <c- n>std</c-><c- o>::</c-><c- n>meta</c-><c- o>::</c-><c- n>info</c-> <c- n>S_p</c-> <c- o>=</c-> <c- n>get_member</c-><c- p>(</c-><c- o>^^</c-><c- n>S</c-><c- p>,</c-> <c- s>"p"</c-><c- p>);</c->

    <c- c1>// Accessing private data without permission is rejected:</c->
    <c- b>int</c-> <c- n>s_p</c-> <c- o>=</c-> <c- n>s</c-><c- p>.[</c-><c- o>:</c-> <c- n>S_p</c-> <c- o>:</c-><c- p>];</c->
<c- p>}</c->
</pre>
   <p>This is the authors' preferred solution. It prevents the problematic data access, while permitting the useful metadata access. The changes to standard are minimal. If the calculus for private data access changes in the C++29 timeframe, it is easy to lift this restriction.</p>
   <h4 class="heading settled" data-level="6.1.2" id="proposal_options_context"><span class="secno">6.1.2. </span><span class="content"><code class="highlight"><c- n>access_context</c-><c- o>::</c-><c- n>unchecked</c-><c- p>()</c-></code></span><a class="self-link" href="#proposal_options_context"></a></h4>
   <p><a href="https://isocpp.org/files/papers/P3547R0.html">[P3547R0]</a> proposes the addition of <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>meta</c-><c- o>::</c-><c- n>access_context</c-></code> to describe the permissions for accessing members of types. Included in that paper is <code class="highlight"><c- n>access_context</c-><c- o>::</c-><c- n>unchecked</c-><c- p>()</c-></code> to produce an access context with unrestricted access to all members.</p>
   <p>Accepting <a href="https://isocpp.org/files/papers/P3547R0.html">[P3547R0]</a> but removing <code class="highlight"><c- n>access_context</c-><c- o>::</c-><c- n>unchecked</c-><c- p>()</c-></code> with no replacement also meets the primary goal of this paper, but unfortunately prevents access to private metadata. The changes to the standard are minimal.</p>
   <h3 class="heading settled" data-level="6.2" id="proposal_useful"><span class="secno">6.2. </span><span class="content">Useful reflection without unrestricted access</span><a class="self-link" href="#proposal_useful"></a></h3>
   <p>If access breaking is removed from C++26, the inevitable question is how to achieve all those common tasks involving private members that reflection is supposed to solve.</p>
   <p>Using the existing C++ rules, users could simply opt-in using friend declarations.</p>
   <h4 class="heading settled" data-level="6.2.1" id="proposal_useful_splicing"><span class="secno">6.2.1. </span><span class="content">Access-aware splicing</span><a class="self-link" href="#proposal_useful_splicing"></a></h4>
   <p>If option 1. along with <a href="https://isocpp.org/files/papers/P3547R0.html">[P3547R0]</a> (or something like it) is accepted, in the future the splicing syntax could be extended to take an optional access context object along with the <code class="highlight"><c- n>meta</c-><c- o>::</c-><c- n>info</c-></code>:</p>
<pre class="highlight"><c- c1>// Splice using access of the specified context:</c->
<c- n>x</c-><c- p>.[</c-><c- o>:</c-> <c- n>private_member_info</c-><c- p>,</c-> <c- n>context</c-> <c- o>:</c-><c- p>]</c->
</pre>
   <h4 class="heading settled" data-level="6.2.2" id="proposal_useful_decorators"><span class="secno">6.2.2. </span><span class="content">Metaclass decorators</span><a class="self-link" href="#proposal_useful_decorators"></a></h4>
   <p>This example uses a Python-like syntax for decorators that could be introduced in the future and used to transform class definitions in a way similar to the metaclass proposal <a data-link-type="biblio" href="#biblio-p0707r5" title="Metaclass functions for generative C++">[P0707R5]</a>.</p>
<pre class="highlight">@<c- n>serializer_lib</c-><c- o>::</c-><c- n>serializable</c->
<c- k>class</c-> <c- nc>my_class</c-> <c- p>{</c->
    <c- b>int</c-> <c- n>private_data_to_serialize</c-><c- p>;</c->

    <c- p>[[</c-><c- o>=</c-><c- n>serializer_lib</c-><c- o>::</c-><c- n>transient</c-><c- p>]]</c->
    <c- b>void</c-><c- o>*</c-> <c- n>transient_pointer</c-> <c- o>=</c-> <c- k>nullptr</c-><c- p>;</c->

<c- k>public</c-><c- o>:</c->
    <c- c1>// Public members...</c->
<c- p>};</c->
</pre>
   <p>The <code class="highlight">@<c- n>serializer_lib</c-><c- o>::</c-><c- n>serializable</c-></code> decorator could be used to automatically transform the class definition in the following way, among other possibilities:</p>
<pre class="highlight"><c- k>class</c-> <c- nc>my_class</c-> <c- p>{</c->
    <c- b>int</c-> <c- n>private_data_to_serialize</c-><c- p>;</c->

    <c- p>[[</c-><c- o>=</c-><c- n>serializer_lib</c-><c- o>::</c-><c- n>transient</c-><c- p>]]</c->
    <c- b>void</c-><c- o>*</c-> <c- n>transient_pointer</c-> <c- o>=</c-> <c- k>nullptr</c-><c- p>;</c->

<c- k>public</c-><c- o>:</c->
    <c- c1>// Public members...</c->

<c- k>private</c-><c- o>:</c->
    <c- c1>// Ensure that the library is able to construct the object.</c->
    <c- n>my_class</c-><c- p>(</c-><c- n>serializer_lib</c-><c- o>::</c-><c- n>deserializer</c-><c- o>&amp;</c-> <c- n>d</c-><c- p>)</c->
        <c- o>:</c-> <c- n>private_data_to_serialize</c-><c- p>(</c-><c- n>d</c-><c- p>.</c-><c- n>deserialize</c-><c- o>&lt;</c-><c- b>int</c-><c- o>></c-><c- p>())</c->
    <c- p>{}</c->

    <c- c1>// Ensure that the library is able to access private members.</c->
    <c- k>friend</c-> <c- n>serializer_lib</c-><c- o>::</c-><c- n>access</c-><c- p>;</c->
<c- p>};</c->
</pre>
   <p>Note that in this case thanks to the generated code, befriending the serializer is not strictly necessary. Alternative protocols could be established in order to allow the serializer library to construct the class during deserialisation.</p>
   <p>This closely resembles Rust’s much praised serialization framework Serde:</p>
<pre class="highlight"><c- cp>#[derive(Serialize, Deserialize, Debug)]</c->
<c- k>struct</c-> <c- nc>Point</c-> <c- p>{</c->
    <c- nl>x</c-><c- p>:</c-> <c- n>i32</c-><c- p>,</c->
    <c- nl>y</c-><c- p>:</c-> <c- n>i32</c-><c- p>,</c->
<c- p>}</c->
</pre>
  </main>
<script>
(function() {
  "use strict";
  var collapseSidebarText = '<span aria-hidden="true">←</span> '
                          + '<span>Collapse Sidebar</span>';
  var expandSidebarText   = '<span aria-hidden="true">→</span> '
                          + '<span>Pop Out Sidebar</span>';
  var tocJumpText         = '<span aria-hidden="true">↑</span> '
                          + '<span>Jump to Table of Contents</span>';

  var sidebarMedia = window.matchMedia('screen and (min-width: 78em)');
  var autoToggle   = function(e){ toggleSidebar(e.matches) };
  if(sidebarMedia.addListener) {
    sidebarMedia.addListener(autoToggle);
  }

  function toggleSidebar(on) {
    if (on == undefined) {
      on = !document.body.classList.contains('toc-sidebar');
    }

    /* Don't scroll to compensate for the ToC if we're above it already. */
    var headY = 0;
    var head = document.querySelector('.head');
    if (head) {
      // terrible approx of "top of ToC"
      headY += head.offsetTop + head.offsetHeight;
    }
    var skipScroll = window.scrollY < headY;

    var toggle = document.getElementById('toc-toggle');
    var tocNav = document.getElementById('toc');
    if (on) {
      var tocHeight = tocNav.offsetHeight;
      document.body.classList.add('toc-sidebar');
      document.body.classList.remove('toc-inline');
      toggle.innerHTML = collapseSidebarText;
      if (!skipScroll) {
        window.scrollBy(0, 0 - tocHeight);
      }
      tocNav.focus();
      sidebarMedia.addListener(autoToggle); // auto-collapse when out of room
    }
    else {
      document.body.classList.add('toc-inline');
      document.body.classList.remove('toc-sidebar');
      toggle.innerHTML = expandSidebarText;
      if (!skipScroll) {
        window.scrollBy(0, tocNav.offsetHeight);
      }
      if (toggle.matches(':hover')) {
        /* Unfocus button when not using keyboard navigation,
           because I don't know where else to send the focus. */
        toggle.blur();
      }
    }
  }

  function createSidebarToggle() {
    /* Create the sidebar toggle in JS; it shouldn't exist when JS is off. */
    var toggle = document.createElement('a');
      /* This should probably be a button, but appearance isn't standards-track.*/
    toggle.id = 'toc-toggle';
    toggle.class = 'toc-toggle';
    toggle.href = '#toc';
    toggle.innerHTML = collapseSidebarText;

    sidebarMedia.addListener(autoToggle);
    var toggler = function(e) {
      e.preventDefault();
      sidebarMedia.removeListener(autoToggle); // persist explicit off states
      toggleSidebar();
      return false;
    }
    toggle.addEventListener('click', toggler, false);


    /* Get <nav id=toc-nav>, or make it if we don't have one. */
    var tocNav = document.getElementById('toc-nav');
    if (!tocNav) {
      tocNav = document.createElement('p');
      tocNav.id = 'toc-nav';
      /* Prepend for better keyboard navigation */
      document.body.insertBefore(tocNav, document.body.firstChild);
    }
    /* While we're at it, make sure we have a Jump to Toc link. */
    var tocJump = document.getElementById('toc-jump');
    if (!tocJump) {
      tocJump = document.createElement('a');
      tocJump.id = 'toc-jump';
      tocJump.href = '#toc';
      tocJump.innerHTML = tocJumpText;
      tocNav.appendChild(tocJump);
    }

    tocNav.appendChild(toggle);
  }

  var toc = document.getElementById('toc');
  if (toc) {
    createSidebarToggle();
    toggleSidebar(sidebarMedia.matches);

    /* If the sidebar has been manually opened and is currently overlaying the text
       (window too small for the MQ to add the margin to body),
       then auto-close the sidebar once you click on something in there. */
    toc.addEventListener('click', function(e) {
      if(e.target.tagName.toLowerCase() == "a" && document.body.classList.contains('toc-sidebar') && !sidebarMedia.matches) {
        toggleSidebar(false);
      }
    }, false);
  }
  else {
    console.warn("Can't find Table of Contents. Please use <nav id='toc'> around the ToC.");
  }

  /* Wrap tables in case they overflow */
  var tables = document.querySelectorAll(':not(.overlarge) > table.data, :not(.overlarge) > table.index');
  var numTables = tables.length;
  for (var i = 0; i < numTables; i++) {
    var table = tables[i];
    var wrapper = document.createElement('div');
    wrapper.className = 'overlarge';
    table.parentNode.insertBefore(wrapper, table);
    wrapper.appendChild(table);
  }

})();
</script>
  <h2 class="no-num no-ref heading settled" id="references"><span class="content">References</span><a class="self-link" href="#references"></a></h2>
  <h3 class="no-num no-ref heading settled" id="informative"><span class="content">Informative References</span><a class="self-link" href="#informative"></a></h3>
  <dl>
   <dt id="biblio-n3980">[N3980]
   <dd>H. Hinnant, V. Falco, J. Byteway. <a href="https://wg21.link/n3980"><cite>Types don't know #</cite></a>. 24 May 2014. URL: <a href="https://wg21.link/n3980">https://wg21.link/n3980</a>
   <dt id="biblio-p0707r5">[P0707R5]
   <dd>Herb Sutter. <a href="https://wg21.link/p0707r5"><cite>Metaclass functions for generative C++</cite></a>. 16 October 2024. URL: <a href="https://wg21.link/p0707r5">https://wg21.link/p0707r5</a>
   <dt id="biblio-p2996r8">[P2996R8]
   <dd>Barry Revzin, Wyatt Childers, Peter Dimov, Andrew Sutton, Faisal Vali, Daveed Vandevoorde, Dan Katz. <a href="https://wg21.link/p2996r8"><cite>Reflection for C++26</cite></a>. 17 December 2024. URL: <a href="https://wg21.link/p2996r8">https://wg21.link/p2996r8</a>
   <dt id="biblio-p3081r0">[P3081R0]
   <dd>Herb Sutter. <a href="https://wg21.link/p3081r0"><cite>Core safety Profiles: Specification, adoptability, and impact</cite></a>. 16 October 2024. URL: <a href="https://wg21.link/p3081r0">https://wg21.link/p3081r0</a>
   <dt id="biblio-p3125r0">[P3125R0]
   <dd>Hana Dusíková. <a href="https://wg21.link/p3125r0"><cite>Pointer tagging</cite></a>. 22 May 2024. URL: <a href="https://wg21.link/p3125r0">https://wg21.link/p3125r0</a>
   <dt id="biblio-p3390r0">[P3390R0]
   <dd>Sean Baxter, Christian Mazakas. <a href="https://wg21.link/p3390r0"><cite>Safe C++</cite></a>. 12 September 2024. URL: <a href="https://wg21.link/p3390r0">https://wg21.link/p3390r0</a>
   <dt id="biblio-p3394r0">[P3394R0]
   <dd>Daveed Vandevoorde, Wyatt Childers, Dan Katz,. <a href="https://wg21.link/p3394r0"><cite>Annotations for Reflection</cite></a>. 14 October 2024. URL: <a href="https://wg21.link/p3394r0">https://wg21.link/p3394r0</a>
   <dt id="biblio-p3473r0">[P3473R0]
   <dd>Steve Downey. <a href="https://wg21.link/p3473r0"><cite>Splicing Should Respect Access Control</cite></a>. 16 October 2024. URL: <a href="https://wg21.link/p3473r0">https://wg21.link/p3473r0</a>
  </dl>