<!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>P2964R1: Adding support for user-defined element types (UDT) in &lt;code>std::simd&lt;/code></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 74068a90b, updated Wed Mar 22 17:34:51 2023 -0700" name="generator">
  <link href="http://wg21.link/P2964R1" rel="canonical">
  <link href="https://isocpp.org/favicon.ico" rel="icon">
  <meta content="d1a03589762505209c8574d1e594b8d562e8d853" name="document-revision">
<style>
.ins, ins, ins * {
    background-color: rgb(200, 250, 200);
    color: rgb(0, 136, 0);
    text-decoration: none;
}
.span, span, span * {
    white-space: pre
}
</style>
<style type="text/css">
.tg  {border-collapse:collapse;border-spacing:0;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
  overflow:hidden;padding:10px 5px;word-break:normal;}
.tg th{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
  font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-0pky{border-color:inherit;text-align:left;vertical-align:top}
.tg .tg-61xu{background-color:#cbcefb;border-color:inherit;text-align:left;vertical-align:top}
</style>
<style>/* 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;
}
</style>
<style>/* 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;
}
</style>
<style>/* 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>/* style-issues */
a[href].issue-return {
    float: right;
    float: inline-end;
    color: var(--issueheading-text);
    font-weight: bold;
    text-decoration: none;
}
</style>
<style>/* 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>/* 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>/* 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 */
</style>
<style>/* style-darkmode */

@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; }
}

@media (prefers-color-scheme: dark) {
    :root {
        --selflink-text: black;
        --selflink-bg: silver;
        --selflink-hover-text: white;
    }
}

@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">P2964R1<br>Adding support for user-defined element types (UDT) in <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code></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="2024-05-22">2024-05-22</time></span></h2>
   <div data-fill-with="spec-metadata">
    <dl>
     <dt>This version:
     <dd><a class="u-url" href="http://wg21.link/P2964R1">http://wg21.link/P2964R1</a>
     <dt class="editor">Authors:
     <dd class="editor p-author h-card vcard"><a class="p-name fn u-email email" href="mailto:daniel.towner@intel.com">Daniel Towner</a> (<span class="p-org org">Intel</span>)
     <dd class="editor p-author h-card vcard"><a class="p-name fn u-email email" href="mailto:ruslan.arutyunyan@intel.com">Ruslan Arutyunyan</a> (<span class="p-org org">Intel</span>)
     <dt>Audience:
     <dd>LEWG
     <dt>Project:
     <dd>ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21
    </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>The paper proposes to support user-defined element types in <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> using customization points</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="#intro"><span class="secno">1</span> <span class="content">Motivation</span></a>
    <li>
     <a href="#background"><span class="secno">2</span> <span class="content">Understanding customization opportunities, requirements, and restrictions</span></a>
     <ol class="toc">
      <li><a href="#understanding_type_restrictions"><span class="secno">2.1</span> <span class="content">Type restrictions</span></a>
      <li><a href="#customisation_classification"><span class="secno">2.2</span> <span class="content">Customisation point classifications</span></a>
      <li><a href="#required_cp"><span class="secno">2.3</span> <span class="content">Required customization points</span></a>
     </ol>
    <li>
     <a href="#customization_framework"><span class="secno">3</span> <span class="content">Creating a customization framework</span></a>
     <ol class="toc">
      <li><a href="#opt_in_out"><span class="secno">3.1</span> <span class="content">Opt-in or opt-out</span></a>
      <li><a href="#storage_customization"><span class="secno">3.2</span> <span class="content">Storage</span></a>
      <li><a href="#operator_customization"><span class="secno">3.3</span> <span class="content">Unary and binary operator customization points</span></a>
      <li><a href="#conversion_customization"><span class="secno">3.4</span> <span class="content">Conversion customization</span></a>
      <li><a href="#free_function_customization"><span class="secno">3.5</span> <span class="content">Overloads for free-functions</span></a>
     </ol>
    <li><a href="#extended_standard_support"><span class="secno">4</span> <span class="content">Extending support to <code class="highlight"><c- k>enum</c-></code> and <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>byte</c-></code></span></a>
    <li>
     <a href="#implementation_experience"><span class="secno">5</span> <span class="content">Implementation Experience</span></a>
     <ol class="toc">
      <li><a href="#summary_implementation_experience"><span class="secno">5.1</span> <span class="content">Summary of implementation experience</span></a>
     </ol>
    <li><a href="#future_work"><span class="secno">6</span> <span class="content">Future work</span></a>
    <li><a href="#conclusion"><span class="secno">7</span> <span class="content">Conclusion</span></a>
    <li><a href="#acknowledgements"><span class="secno">8</span> <span class="content">Acknowledgements</span></a>
    <li><a href="#revision_history"><span class="secno">9</span> <span class="content">Revision History</span></a>
    <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="intro"><span class="secno">1. </span><span class="content">Motivation</span><a class="self-link" href="#intro"></a></h2>
   <p>ISO/IEC 19570:2018 (1) introduced data-parallel types to the C++ Extensions for
Parallelism TS. <a data-link-type="biblio" href="#biblio-p1928r8" title="std::simd - Merge data-parallel types from the Parallelism TS 2">[P1928R8]</a> is proposing to make that a part of C++ IS. In the
current proposal individual elements must be of a type which satisfies
constraints set out by <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> (e.g., arithmetic types, complex-value
types). In this document we describe how <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> could allow user-defined
element types too, with automatic generation of element-wise operators for
user-defined types which define those operators, and with the option of dispatch
of operations to customization functions which allow the user to provide more
efficient implementations when automatic generation does less well.</p>
   <p>Being able to have user-defined types in a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> value is desirable
because it allows us to build on top of the standard features of <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> to
support SIMD programming in specialised problem domains, such as signal or media
processing, which might have custom data types (e.g., saturating or fixed-point
types). The idea is that <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> will be used to provide generic SIMD
capabilities for performing loads, stores, masking, reductions, gathers,
scatters, and much more, and then a set of customization points are provided to
allow the fundamental arithmetic operators to be overloaded where needed.</p>
   <p>As a concrete example, consider that we might have a fixed-point data type for
signal processing called <code class="highlight"><c- n>fixed_point_16s8</c-></code>. Such a fixed-point data type allows
a fractional (non-integer) value to be stored with only a fixed number of digits
to represent their fractional part. An instruction set which targets digital
signal processing will be likely to have instructions which accelerate the fundamental
arithmetic operations for these types and our aim is to allow <code class="highlight"><c- n>simd</c-></code> values of
this underlying fixed-point element type to be created and used. To begin with,
a <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>fixed_point_16s8</c-><c- o>></c-></code> is represented or stored using individual blocks of bits
representing each element of the user-defined type within a vector register:</p>
   <p><img src="data:image/png;base64,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" width="90%"></p>
   <p>Note that a selected element, highlighted with a red rectangle, represents an individual <code class="highlight"><c- n>fixed_point_16s8</c-></code> element value. The colours and values in each box represent
specific bit patterns. Where two elements have the same bit-pattern/colour, they
represent the same element value. An operation which moves elements within or
across <code class="highlight"><c- n>simd</c-></code> objects, or to and from memory, will copy the bit patterns. The
elements don’t change value because they move. In the diagram below a <code class="highlight"><c- n>permute</c-></code> has been used to extract the even elements of our original example above.</p>
   <p><img src="data:image/png;base64,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" width="70%"></p>
   <p>Note that an element representing a specific value in this example (e.g., the
dark-blue value 12) continues to represent the value 12 regardless of where it
appears within the <code class="highlight"><c- n>simd</c-></code> object. However, there are also cases where the bit
pattern of individual elements does need to be interpreted as a specific value.
For example, if we wish to add two <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>fixed_point_16s8</c-><c- o>></c-></code> values together then
we need to define how to efficiently handle the addition of specific bit
patterns to form a new bit pattern.  In the example below we are adding two <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>fixed_point_16s8</c-><c- o>></c-></code> values and expecting to get a result which is created
by calling some underlying target-specific function:</p>
   <p><img src="data:image/png;base64,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" width="70%"></p>
   <p>In this example the <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> objects themselves have no knowledge of this
since the elements are of a custom type, so we need to encode that knowledge
into a user-defined customisation point. There will be a customisation point in <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> in those places where knowledge of what the bit-pattern in a
user-defined <code class="highlight"><c- n>simd</c-></code> element actually means. Common examples of such points are
arithmetic operations or relational comparators. In the example above, a
customisation point for <code class="highlight"><c- n>simd</c-><c- o>::</c-><c- k>operator</c-><c- o>+</c-></code> will be provided to map onto a
suitable hardware instruction to perform a vector fixed-point addition.</p>
   <p>Custom types will not always support every possible operator that is exposed by <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code>. For example, perhaps a <code class="highlight"><c- n>fixed_point_16s8</c-></code> doesn’t allow division or
modulus, in which case <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-><c- o>::</c-><c- k>operator</c-><c- o>/</c-></code> and <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-><c- o>::</c-><c- k>operator</c-><c- o>%</c-></code> must be
removed from the overload set entirely.</p>
   <p>Putting these mechanisms for defining custom element types together allows
us to write generic functions which can then be invoked with both built-in and
user-defined types equally easily, using the rich API of <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code>, even when
the user-defined types are using target-specific customised functions:</p>
<pre class="highlight"><c- c1>// Compute the dot-product of a simd value.</c->
<c- k>template</c-><c- o>&lt;</c-><c- k>typename</c-> <c- nc>T</c-><c- p>,</c-> <c- k>typename</c-> <c- nc>ABI</c-><c- o>></c->
<c- k>auto</c-> <c- n>dotprod</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_simd</c-><c- o>&lt;</c-><c- n>T</c-><c- p>,</c-> <c- n>ABI</c-><c- o>>&amp;</c-> <c- n>lhs</c-><c- p>,</c-> <c- k>const</c-> <c- n>basic_simd</c-><c- o>&lt;</c-><c- n>T</c-><c- p>,</c-> <c- n>ABI</c-><c- o>>&amp;</c-> <c- n>rhs</c-><c- p>)</c-> <c- p>{</c->
  <c- c1>// The reduction moves are handled bit-wise, while the multiply and addition are delegated</c->
  <c- c1>// to target-specific customisation points. The high-level code is unaltered.</c->
  <c- k>return</c-> <c- n>reduce</c-><c- p>(</c-><c- n>lhs</c-> <c- o>*</c-> <c- n>rhs</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>plus</c-><c- o>&lt;></c-><c- p>{});</c->
<c- p>}</c->

<c- p>...</c->
<c- b>float</c-> <c- n>dfp</c-> <c- o>=</c-> <c- n>dotprod</c-><c- p>(</c-><c- n>simdFloat0</c-><c- p>,</c-> <c- n>simdFloat1</c-><c- p>);</c->
<c- n>fixed_point_16s8</c-> <c- n>dfxp</c-> <c- o>=</c-> <c- n>dotprod</c-><c- p>(</c-><c- n>simdFixed0</c-><c- p>,</c-> <c- n>simdFixed1</c-><c- p>);</c-></pre>
   <p>An extended example of some custom element types is given toward the end of this
paper, along with our experiences with using them.</p>
   <p>This proposed extension of <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> to support custom types applies only to
types which can be vectorised as atomic units. That is, a <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-></code> for a custom
type <code class="highlight"><c- n>T</c-></code> has storage which is the same as an array of those types (i.e., this
might be called an array-of-structures). This is in contrast to another way of
representing a parallel collection of custom element types in which the
structure of the custom element is broken down into individual pieces (i.e., a
structure-of-arrays). For example, given <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- n>pair</c-><c- o>&lt;</c-><c- n>A</c-><c- p>,</c-> <c- n>B</c-><c- o>>></c-></code> the
structure-of-array style of customisation would treat it as though it were <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>pair</c-><c- o>&lt;</c-><c- n>simd</c-><c- o>&lt;</c-><c- n>A</c-><c- o>></c-><c- p>,</c-> <c- n>simd</c-><c- o>&lt;</c-><c- n>B</c-><c- o>>></c-></code>. While that could be a valuable feature for future
consideration, it is different to what is being proposed in this paper.</p>
   <p>In addition to allowing customized element types in <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code>, a side-effect
of this paper is to set out a way of thinking about the meaning of different <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> operations which makes the division of responsibility of different <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> APIs clear. This makes it easier to discuss the related topic of
adding new C++ builtin element types such as <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>byte</c-></code>, and scoped/unscoped
enumerations. For example, as we shall describe later there are specific basis
functions which define the fundamental arithmetic behaviour of <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> types, and knowing what those basis functions should be makes it easy to reason
about how to support <code class="highlight"><c- k>enum</c-></code> and <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>byte</c-></code> types. Later in this document we
shall introduce the necessary changes to allow <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- k>enum</c-><c- o>></c-></code> and <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>byte</c-><c- o>></c-></code> to
be easily incorporated.</p>
   <p>Our intent with user-defined types is that the underlying type behaves like a
value type as defined in Elements of Programming (e.g., integers represented as
two’s complement values, or rational numbers represented as a concatenation of
two 32-bit sequences, interpreted as integer numerator and denominator). Only
arithmetic operations can be applied to value-types. <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> does not
provide overloads for non-value-type operators, such as the member-related
operators like <code class="highlight"><c- k>operator</c-><c- o>-></c-></code>, <code class="highlight"><c- k>operator</c-><c- p>,</c-></code>. We do not intend to extend <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> to include these non-value operators.</p>
   <p>Since <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> is built on top of native hardware support, there are certain
limitations in the hardware that prevent arbitrary types being representable in
a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code>. It will be necessary to impose some restrictions on the types of
elements to make them implementable.</p>
   <p>Note that a user-defined type, such as our example <code class="highlight"><c- n>fixed_point_16s8</c-></code>, might
provide its own overloaded operators for handling the different types of
arithmetic operation that could occur. Ideally we want the operators from that
scalar type to be mapped over to work for <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>fixed_point_16s8</c-><c- o>></c-></code> as well. In
effect, every <code class="highlight"><c- n>simd</c-></code> operator for that type would perform the equivalent
element-wise operator on the underlying values. For example, suppose we have the
following partially-implemented scalar <code class="highlight"><c- n>fixed_point_16s8</c-></code> type:</p>
<pre class="highlight"><c- k>struct</c-> <c- nc>fixed_point_16s8</c-> <c- p>{</c->

    <c- n>fixed_point_16s8</c-> <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>fixed_point_16s8</c-> <c- n>lhs</c-><c- p>,</c-> <c- n>fixed_point_16s8</c-> <c- n>rhs</c-><c- p>)</c->
      <c- p>{</c-> <c- k>return</c-> <c- n>__intrin_fixed_add</c-><c- p>(</c-><c- n>lhs</c-><c- p>,</c-> <c- n>rhs</c-><c- p>);</c-> <c- p>}</c->
    <c- n>fixed_point_16s8</c-> <c- k>operator</c-><c- o>-</c-><c- p>(</c-><c- n>fixed_point_16s8</c-> <c- n>lhs</c-><c- p>,</c-> <c- n>fixed_point_16s8</c-> <c- n>rhs</c-><c- p>)</c->
      <c- p>{</c-> <c- k>return</c-> <c- n>__intrin_fixed_sub</c-><c- p>(</c-><c- n>lhs</c-><c- p>,</c-> <c- n>rhs</c-><c- p>);</c-> <c- p>}</c->

    <c- b>int</c-> <c- n>do_special_op</c-><c- p>()</c-> <c- k>const</c-><c- p>;</c->

    <c- n>std</c-><c- o>::</c-><c- b>int16_t</c-> <c- n>data</c-><c- p>;</c->
<c- p>};</c-></pre>
   <p>If the user instantiated <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>fixed_point_16s8</c-><c- o>></c-></code> and performed addition between
two values of this parallel type then this should give the same result as
invoking the operator above on each element in turn. The automatic extension of
a scalar operator to a simd operator is the first level of support that <code class="highlight"><c- n>simd</c-><c- o>&lt;></c-></code> should provide. Unfortunately, there may be cases where the compiler does a poor
job of auto-vectorising such code; iterating over each element in
turn does not automatically make good simd code. For these cases we also need a
mechanism which allows the automatic operator inferencing to be overridden to
replace it with a more efficient method provided by the user. We will use the
idea of customisation points, as used in other parts of C++, to enable this. The
user will be able to provide a special function for specific simd operators to
provide efficient implementations for those cases where the compiler doesn’t
find this for itself.</p>
   <p>In the example above we also had a class method. At the moment no mechanism exists in
C++ to allow us to automatically add the equivalent method to <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>fixed_point_16s8</c-><c- o>></c-></code> so we cannot create a complex simd-generic extension of
the user-defined type yet. In future if suitable reflection capabilities are
added this extension may become possible, but this is currently out of scope for this
paper.</p>
   <h2 class="heading settled" data-level="2" id="background"><span class="secno">2. </span><span class="content">Understanding customization opportunities, requirements, and restrictions</span><a class="self-link" href="#background"></a></h2>
   <p>The first step in understanding how <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> can be extended to new element
types is to review what operations are provided by <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code>, and how those operations must adapt to operating on custom user-defined elements.</p>
   <h3 class="heading settled" data-level="2.1" id="understanding_type_restrictions"><span class="secno">2.1. </span><span class="content">Type restrictions</span><a class="self-link" href="#understanding_type_restrictions"></a></h3>
   <p>The current proposal for <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> in <a data-link-type="biblio" href="#biblio-p1928r8" title="std::simd - Merge data-parallel types from the Parallelism TS 2">[P1928R8]</a> only allows selected types
to be stored in a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> but our proposal for user-defined types makes it
possible to theoretically store any other C++ type. It may not make sense to be
able to store some of those other types in a <code class="highlight"><c- n>simd</c-></code>. For example, they may have
side-effects that means they don’t work as expected when operated on in parallel,
or they rely on features that don’t translate well to a SIMD instruction set. We
should seek to restrict the valid element types to avoid the worst issues that
might arise.</p>
   <p>Although <a data-link-type="biblio" href="#biblio-p1928r8" title="std::simd - Merge data-parallel types from the Parallelism TS 2">[P1928R8]</a> makes no mention of it, there is an implication that the
elements in a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> are trivially copyable.  For operations which move
elements around - broadcast, permutation, gather, scatter, and so on - it is
assumed that when the bits move location within a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> object they will
continue to represent their original value. <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> currently restricts
types to be floating-point, integral, or complex-valued (with <a data-link-type="biblio" href="#biblio-p2663r4" title="Proposal to support interleaved complex values in std::simd">[P2663R4]</a>), all
of which are trivially copyable.</p>
   <p>Ultimately the success of <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> is tied to how well the underlying
hardware can support the element type. All known hardware supports only
elements which have sizes which are power-of-2, so we propose to require <code class="highlight"><c- n>simd</c-></code> element types to respect this. Also, most hardware has a limit on the size of
individual elements. In the current proposal of <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code>, the largest
possible element type is the 128-bit <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>complex</c-><c- o>&lt;</c-><c- b>double</c-><c- o>></c-></code>, so we will set this
as the upper limit of user-defined elements. Therefore, any user-defined type
for <code class="highlight"><c- n>simd</c-></code> must be 1, 2, 4, 8 or 16 bytes in size.</p>
   <p><code class="highlight"><c- n>simd</c-></code> was designed to work on arithmetic value-like types, in common with the
hardware instruction sets to which they are ultimately giving access.
Restricting <code class="highlight"><c- n>simd</c-></code> to only such types is difficult as C++ has no way of querying
a type to determine if it is a product or sum type. Instead, we will only extend
user-defined operators to those operators which <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> already supports
(e.g., <code class="highlight"><c- k>operator</c-><c- o>+</c-></code>). We will not provide additional operators to support
member-like access (e.g., <code class="highlight"><c- k>operator</c-><c- o>->*</c-></code>), dereferencing (e.g., unary <code class="highlight"><c- k>operator</c-><c- o>*</c-></code>) since the presence of these operators implies they aren’t suitable
for storing in a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> anyway</p>
   <p>We propose that certain types should always be banned as <code class="highlight"><c- n>simd</c-></code> elements
including <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- k>union</c-><c- o>></c-></code>, and any sort of pointer element. In the proposal we
will also explore the use of an opt-out mechanism which can be used to prevent
certain types from ever being contained in a <code class="highlight"><c- n>simd</c-></code>.</p>
   <h3 class="heading settled" data-level="2.2" id="customisation_classification"><span class="secno">2.2. </span><span class="content">Customisation point classifications</span><a class="self-link" href="#customisation_classification"></a></h3>
   <p>While many operations within <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> will work on trivially copyable custom
types there are also places where <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> does need to interpret the meaning
of the bits, and it is those that need to be customization points. Each function can be put into one of the following categories:</p>
   <dl>
    <dt data-md>Basis
    <dd data-md>
     <p>A basis function is one that must be provided as a customization point to allow the underlying element type to be used. An example would be addition; if addition is not provided as a customization point for a user-defined type then <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> values of that type cannot be added together.</p>
    <dt data-md>Custom
    <dd data-md>
     <p>A customization function is one that can be implemented generically but which can also be customized to provide a more efficient implementation if one exists. An example of a Custom function would be negate (<code class="highlight"><c- k>operator</c-><c- o>-</c-></code>) which could have a default implementation which subtracts from zero, or could be customized if the type provides a faster alternative (e.g., sign-bit flip for a floating-point-like type).</p>
    <dt data-md>Copy
    <dd data-md>
     <p>A copy function uses the trivially copyable nature of the underlying type and allows bits to be moved from one place to another. The <code class="highlight"><c- n>permute</c-></code> function is a good example of a Copy function since a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> of any type can move its elements around within a SIMD value without needing to know what the bits represent.</p>
    <dt data-md>Algorithm
    <dd data-md>
     <p>An algorithm function uses other functions to implement some feature. If the algorithm relies on a Basis or Custom function which is not provided by the user-defined type then the algorithm function is removed from the overload set. An algorithm function does not provide a customization point.</p>
   </dl>
   <p>The following table lists the key functions in <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code>, and what category
they fall into. The table allows us to reason about what functions in <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> will just work as they are currently defined, and to separate out
those functions which must have customisation points defined in order to allow
their behaviour to be changed for custom user-defined types.</p>
   <table class="tg">
    <thead>
     <tr>
      <th class="tg-0pky">Function
      <th class="tg-0pky">Type
      <th class="tg-0pky">Notes
    <tbody>
     <tr>
      <td class="tg-61xu" colspan="3"><code class="highlight"><c- n>simd_mask</c-></code>
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>simd_mask</c-> <c- p>{}</c-></code>
      <td class="tg-0pky">n/a
      <td class="tg-0pky">Virtually every function in <code class="highlight"><c- n>simd_mask</c-></code> will work on user-defined types, with the exception of those listed in the next row. The mask is only dependent on knowing the number of bits in the type, and not on the interpretation of those bits. 
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>simd_mask</c-><c- o>::</c-><c- k>operator</c-><c- o>+</c-></code><br><code class="highlight"><c- n>simd_mask</c-><c- o>::</c-><c- k>operator</c-><c- o>-</c-></code><br><code class="highlight"><c- n>simd_mask</c-><c- o>::</c-><c- k>operator</c-><c- o>~</c-></code>
      <td class="tg-0pky">Algorithm
      <td class="tg-0pky">Convert and broadcast a 0, 1 or -1, and perform a <code class="highlight"><c- n>simd_select</c-></code> using the mask to choose the appropriate value. Only provided if the convert is available.
     <tr>
      <td class="tg-61xu" colspan="3">Constructors
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>basic_simd</c-><c- p>(</c-><c- n>U</c-><c- p>)</c-></code>
      <td class="tg-0pky">Copy
      <td class="tg-0pky">Broadcast a copy of the bits from represent the scalar source object to every element
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>basic_simd</c-><c- p>(</c-><c- n>basic_simd</c-><c- o>&lt;</c-><c- n>U</c-><c- o>></c-><c- p>)</c-></code>
      <td class="tg-0pky">Copy/custom
      <td class="tg-0pky">When U is the same as T, direct copy the bits into place.<br>When U is different, we need a customization point to convert the user-defined elements. If no customization point is available, no conversions will be allowed but copying from other <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> of the same type will be permitted.
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>basic_simd</c-><c- p>(</c-><c- n>Gen</c-><c- o>&amp;&amp;</c-><c- p>)</c-></code>
      <td class="tg-0pky">Copy
      <td class="tg-0pky">Each invocation of the generator builds an individual scalar value of the element type which is bitwise copied into the respective element.
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>basic_simd</c-><c- p>(</c-><c- n>Iter</c-><c- p>)</c-></code>
      <td class="tg-0pky">Copy/Algorithm
      <td class="tg-0pky">When <code class="highlight"><c- n>Iter</c-><c- o>::</c-><c- n>value_type</c-></code> is the same, copy the bits.<br>When <code class="highlight"><c- n>Iter</c-><c- o>::</c-><c- n>value_type</c-></code> is different and a customization point exists for the conversion, create the <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> as the value iterator type first, and then copy the bits. No customization point will be allowed since it is unlikely that it brings any performance benefit, although this decision can be revisited.
     <tr>
      <td class="tg-61xu" colspan="3">Copy functions
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>copy_to</c-></code>
      <td class="tg-0pky">Copy/Algorithm
      <td class="tg-0pky">When the destination type is the same use a direct bit copy from the <code class="highlight"><c- n>simd</c-></code> into memory.<br>When the destination type is different, convert to a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> of the destination type and invoke <code class="highlight"><c- n>copy_to</c-></code> on that. A customization point is not provided since it is highly unlikely that any hardware support is available for copying to an special type.<br>If the destination type is different and there is no conversion customization point remove the conversion-copy from the overload set.
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>copy_from</c-></code>
      <td class="tg-0pky">Algorithm
      <td class="tg-0pky">Equivalent to calling <code class="highlight"><c- n>basic_simd</c-><c- p>(</c-><c- n>Iter</c-><c- p>)</c-></code> and performing an assignment.
     <tr>
      <td class="tg-61xu" colspan="3">Subscript operators
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- k>operator</c-><c- p>[]</c-></code>
      <td class="tg-0pky">Copy
      <td class="tg-0pky">Bitwise copy from element into the scalar output value
     <tr>
      <td class="tg-61xu" colspan="3">Unary operators
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- k>operator</c-><c- o>-</c-></code>
      <td class="tg-0pky">Custom
      <td class="tg-0pky">If a customization point or builtin-type support is available use that.<br>Otherwise if <code class="highlight"><c- k>operator</c-><c- o>-</c-></code> is available use <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-><c- p>()</c-> <c- o>-</c-> <c- o>*</c-><c- k>this</c-></code>.<br>Otherwise remove from the overload set.
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- k>operator</c-><c- o>--/++</c-></code>
      <td class="tg-0pky">Algorithm
      <td class="tg-0pky">If <code class="highlight"><c- k>operator</c-><c- o>+/-</c-></code> are available use <code class="highlight"><c- o>*</c-><c- k>this</c-> <c- o>+/-</c-> <c- n>T</c-><c- p>(</c-><c- mi>1</c-><c- p>)</c-></code>.<br>Otherwise remove from the overload set.
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- k>operator</c-><c- o>!</c-></code>
      <td class="tg-0pky">Custom
      <td class="tg-0pky">If a customization point or builtin-type support  is available use that.<br>Otherwise if <code class="highlight"><c- k>operator</c-><c- o>==</c-></code> is available return <code class="highlight"><c- o>*</c-><c- k>this</c-> <c- o>==</c-> <c- n>simd</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-><c- p>()</c-></code>.<br>Otherwise remove from the overload set.
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- k>operator</c-><c- o>~</c-></code>
      <td class="tg-0pky">Basis
      <td class="tg-0pky">If a customization point or builtin-type support is available use that.<br>Otherwise remove from the overload set.
     <tr>
      <td class="tg-61xu" colspan="3">Binary operators
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- k>operator</c-><c- o>+</c-><c- p>,</c-><c- o>-</c-><c- p>,</c-><c- o>*</c-><c- p>,</c-><c- o>/</c-><c- p>,</c-><c- o>%</c-><c- p>,</c-><c- o>&lt;&lt;</c-><c- p>,</c-><c- o>>></c-><c- p>,</c-><c- o>&amp;</c-><c- p>,</c-><c- o>|</c-><c- p>,</c-><c- o>^</c-></code>
      <td class="tg-0pky">Basis
      <td class="tg-0pky">If a customization point or builtin-type support is available use that.<br> Otherwise remove the <code class="highlight"><c- n>simd</c-><c- o>::</c-><c- n>operatorX</c-></code> from the overload set.
     <tr>
      <td class="tg-61xu" colspan="3">Compound assignment operators
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- k>operator</c-><c- o>+=</c-><c- p>,</c-><c- o>-=</c-><c- p>,</c-><c- o>*=</c-><c- p>,</c-><c- o>/=</c-><c- p>,</c-><c- o>%=&amp;=</c-><c- p>,</c-><c- o>|=</c-><c- p>,</c-><c- o>^=</c-><c- p>,</c-><c- o>&lt;&lt;=</c-><c- p>,</c-> <c- o>>>=</c-></code>
      <td class="tg-0pky">Algorithm
      <td class="tg-0pky">If a customization point or builtin-type support is available for the underlying operation use <code class="highlight"><c- o>*</c-><c- k>this</c-> <c- o>=</c-> <c- o>*</c-><c- k>this</c-> <c- n>OP</c-> <c- n>rhs</c-></code>.<br>Otherwise remove this from the overload set.
     <tr>
      <td class="tg-61xu" colspan="3">Relational operators
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- k>operator</c-><c- o>==</c-></code>
      <td class="tg-0pky">Basis
      <td class="tg-0pky">If a customization point or builtin-type support is available call that.<br>Otherwise remove this from the overload set.<br>Note that although each element represents its values using a specific copyable bit pattern this doesn’t mean that the same bit pattern represents an equal value (e.g., floating point NaN bit patterns will never be equal).
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- k>operator</c-><c- o>!=</c-></code>
      <td class="tg-0pky">Custom
      <td class="tg-0pky">If a customization point or builtin-type support is available call that.<br>Otherwise if <code class="highlight"><c- k>operator</c-><c- o>==</c-></code> is provided, return the negation of that function.<br>Otherwise remove from the overload set.
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- k>operator</c-><c- o>&lt;</c-><c- p>,</c-><c- o>&lt;=</c-><c- p>,</c-><c- o>></c-><c- p>,</c-> <c- o>>=</c-></code>
      <td class="tg-0pky">Basis
      <td class="tg-0pky">If a customization point or builtin-type support is available call that.<br>Otherwise remove from the overload set.<br>Note that a minimal set could be provided (e.g., <code class="highlight"><c- k>operator</c-><c- o>&lt;</c-></code> and <code class="highlight"><c- k>operator</c-><c- o>==</c-></code> since everything else can be built from those).
     <tr>
      <td class="tg-61xu" colspan="3">Conditional operator
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>simd_select</c-></code>
      <td class="tg-0pky">Copy
      <td class="tg-0pky">Conditionally copy element bits with no interpretation.
     <tr>
      <td class="tg-61xu" colspan="3">Permute
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>permute</c-></code>/permute-like
      <td class="tg-0pky">Copy
      <td class="tg-0pky">All permutes (generated or dynamic) move bits from one location to another without interpretation. Related operations like resize, insert, extract work in the same way.
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>compress</c-></code>/<code class="highlight"><c- n>expand</c-></code>
      <td class="tg-0pky">Copy
      <td class="tg-0pky">All compression and expansion operations move bits from one location to another without interpretation.
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>gather_from</c-></code>
      <td class="tg-0pky">Copy
      <td class="tg-0pky">Gather the values as though they were a <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>custom</c-><c- o>-</c-><c- n>storage</c-><c- o>-</c-><c- n>type</c-><c- o>></c-></code> and then use <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>bit_cast</c-></code> to convert (at no cost) into a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> of the user defined type.
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>scatter_to</c-></code>
      <td class="tg-0pky">Algorithm
      <td class="tg-0pky">If the same type, bitwise scatter individual elements to the range using direct bitwise copy.<br>If the destination type is different construct a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> of the destination type and perform a scatter on that type instead.
     <tr>
      <td class="tg-61xu" colspan="3">Reductions
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>reduce</c-></code>
      <td class="tg-0pky">Algorithm
      <td class="tg-0pky">All reduction operations can be implemented using a sequence of permutes and arithmetic operations. If the desired operation for the reduction step is not available in the overload set then the corresponding reduction is also removed from the overload set.<br>No customization point will be provided for reductions since it is unlikely that custom types will have hardware support for reductions. These can be added later if this is found to be untrue.
     <tr>
      <td class="tg-61xu" colspan="3">Free functions
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>min</c-></code><br><code class="highlight"><c- n>max</c-></code><br><code class="highlight"><c- n>clamp</c-></code>
      <td class="tg-0pky">Custom
      <td class="tg-0pky">If the user provides their own ADL overloaded customization point for this function then that will be used.<br>Otherwise if relational operators are available for the type, use those to synthesise this operation (i.e., <code class="highlight"><c- n>simd_select</c-><c- p>(</c-><c- n>a</c-> <c- o>&lt;</c-> <c- n>b</c-><c- p>,</c-> <c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>)</c-></code> for <code class="highlight"><c- n>min</c-></code>).<br>Otherwise remove from the overload set.
     <tr>
      <td class="tg-0pky"><code class="highlight"><c- n>abs</c-></code><br><code class="highlight"><c- n>sin</c-></code><br><code class="highlight"><c- n>log</c-></code><br>etc.
      <td class="tg-0pky">Custom
      <td class="tg-0pky">For any other free functions an ADL overload can be provided by the user to handle that specific type.
   </table>
   <h3 class="heading settled" data-level="2.3" id="required_cp"><span class="secno">2.3. </span><span class="content">Required customization points</span><a class="self-link" href="#required_cp"></a></h3>
   <p>In the table of function classifications above we can discount the Copy
functions from any further thought in this proposal. By limiting <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> elements to those which are trivially copyable, we can provide any sort of
operation which moves bits around using the <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> implementation itself, with no
special consideration for user-defined types.</p>
   <p>Unsurprisingly, the table above shows us that we need customization points for
all numeric operations, including:</p>
<pre class="highlight"><c- n>plus</c->          <c- n>minus</c->         <c- n>negate</c->           <c- n>multiplies</c->
<c- n>divides</c->       <c- n>modulus</c->

<c- n>bit</c-> <c- n>AND</c->       <c- n>bit</c-> <c- n>OR</c->        <c- n>bit</c-> <c- n>NOT</c->          <c- n>bit</c-> <c- n>XORr</c->

<c- n>equal</c-> <c- n>to</c->      <c- n>not</c-> <c- n>equal</c-> <c- n>to</c->
<c- n>greater</c->       <c- n>less</c->          <c- n>less</c-> <c- n>or</c-> <c- n>equal</c->    <c- n>greater</c-> <c- n>or</c-> <c- n>equal</c->

<c- n>logical</c-> <c- n>AND</c->   <c- n>logical</c-> <c- n>OR</c->    <c- n>logical</c-> <c- n>NOT</c->

<c- n>shift_left</c->    <c- n>shift_right</c-></pre>
   <p>Also unsurprisingly, these names are all those of the C++ transparent
template wrappers, with the exception of shift-left and shift-right. The only
other customisation point that would be needed is a conversion function. If a
UDT can be constructed or copied from a different type <code class="highlight"><c- n>T</c-></code>, then it should also
be possible to construct or copy a <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>UDT</c-><c- o>></c-></code> from <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-></code> by element-wise
application.</p>
   <p class="note" role="note"><span class="marker">Note:</span> As a small aside, it isn’t clear why transparent operators are not
provided for shift operations, and perhaps they should be added in for
completeness in the future.</p>
   <p>For each of these customisation points we have three layers of behaviour:</p>
   <ul>
    <li data-md>
     <p>Default operator for standard C++ types (e.g., <code class="highlight"><c- b>int</c-></code>, <code class="highlight"><c- b>float</c-></code>, <code class="highlight"><c- n>complex</c-><c- o>&lt;</c-><c- n>floating_point</c-><c- o>></c-></code>). No customisation is possible for any of the types available in C++.</p>
    <li data-md>
     <p>Explicit simd customisation. The user provides a specific function which is used to perform that operation on a <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>UDT</c-><c- o>></c-></code>. The intent is to allow the user to make the operator as efficient as possible for cases where the compiler may not auto-vectorise efficiently.</p>
    <li data-md>
     <p>Implicit <code class="highlight"><c- n>simd</c-></code> customisation using the scalar type’s own operator, applied element-wise. Ideally the compiler will auto-vectorise this to generate efficient code.</p>
    <li data-md>
     <p>Otherwise the operator is removed from the overload set.</p>
   </ul>
   <p>For this last point, an example would be a user-defined complex type which might provide addition, subtraction
and multiplication, but remove modulus, relational operators, and bitwise
operators from the overload set, along with any other operations which depend
upon those (e.g., compound modulus, compound bitwise).</p>
   <p>Conversions will work in this customisation framework in the same manner as
arithmetic operators; if a conversion is not explicitly defined, or the scalar
type doesn’t support it, then the <code class="highlight"><c- n>simd</c-></code> type will also not support that
conversion.</p>
   <h2 class="heading settled" data-level="3" id="customization_framework"><span class="secno">3. </span><span class="content">Creating a customization framework</span><a class="self-link" href="#customization_framework"></a></h2>
   <p>There are several considerations in a framework for user-defined element types: opt-in/out,
storage, unary/binary operators and conversions, and free-functions. In this
section we shall look at each of these in turn.</p>
   <h3 class="heading settled" data-level="3.1" id="opt_in_out"><span class="secno">3.1. </span><span class="content">Opt-in or opt-out</span><a class="self-link" href="#opt_in_out"></a></h3>
   <p>When a <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>UDT</c-><c- o>></c-></code> is created the aim is for library to do as much work as
possible to make that type behave reasonably, subject to a few restrictions
(e.g., element size). All bit-copy operations will work on that type, and any operators
defined for the scalar user-defined type will be mapped into the simd space.</p>
   <p>In the original draft of this proposal the argument was made for an opt-in
process, whereby the user would have to explicitly arrange for the user-defined
type to be permitted as an element of a <code class="highlight"><c- n>simd</c-></code>. Since that first proposal we
have refined the behaviour of simd to allow it to infer simd operators from
scalar operators, thereby making it possible to create a correctly behaving <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>UDT</c-><c- o>></c-></code> with very little effort. With this new approach it seems
unnecessarily onerous to have to require the user to opt-in to something that
works on its own.</p>
   <p>Our new proposal is to have an opt-out mechanism, where the user can explicitly
indicate that a specified type is not suited to being an element in a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code>, even if the type is otherwise legal (e.g., copyable, power-of-2,
smaller than 16 bytes). Such an opt-out mechanism can be used to disable
unsuitable user-defined types as well as other non-vectorisable types in the
standard library, such as <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- n>atomic</c-><c- o>></c-></code>.</p>
   <h3 class="heading settled" data-level="3.2" id="storage_customization"><span class="secno">3.2. </span><span class="content">Storage</span><a class="self-link" href="#storage_customization"></a></h3>
   <p>In order to perform Copy-like operations on <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> elements we need to be
able to inform <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> values of how to store and move the underlying
elements. In <span>[P2964R0]</span> we allowed the user to specify
what the underlying storage should be, but with further thought we have decided
to make the storage unspecified. The <code class="highlight"><c- n>simd</c-></code> implementation is free to choose any
storage type. Customisation points which use that storage will the use <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>bit_cast</c-></code> to convert to and from that storage and the user-defined type
as appropriate.</p>
   <h3 class="heading settled" data-level="3.3" id="operator_customization"><span class="secno">3.3. </span><span class="content">Unary and binary operator customization points</span><a class="self-link" href="#operator_customization"></a></h3>
   <p>There are many different ways to implement customization points, including
template specialization, CPO, or <code class="highlight"><c- n>tag_invoke</c-></code>. Which mechanism is most suitable
can be discussed further if necessary but for this paper proposal we only care
about whether customization should be allowed, not the exact mechanism that will
be used.</p>
   <p>All of the operators for <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> are <code class="highlight"><c- k>friend</c-></code> functions in order to allow ADL.
We must leave these <code class="highlight"><c- k>friend</c-></code> function operators, but allow them to defer to a
customization point as required. One possible pseudo-implementation of an individual
operator may be this:</p>
<pre class="highlight"><c- k>constexpr</c-> <c- k>friend</c-> <c- n>basic_simd</c-> <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_simd</c-><c- o>&amp;</c-> <c- n>lhs</c-><c- p>,</c-> <c- k>const</c-> <c- n>basic_simd</c-><c- o>&amp;</c-> <c- n>rhs</c-><c- p>)</c->
<c- k>requires</c-> <c- p>(</c-><c- n>details</c-><c- o>::</c-><c- n>simd_has_custom_binary_plus</c-> <c- o>||</c-> <c- n>details</c-><c- o>::</c-><c- n>element_has_plus</c-><c- p>)</c->
<c- p>{</c->
    <c- k>if</c-> <c- k>constexpr</c-> <c- p>(</c-><c- n>details</c-><c- o>::</c-><c- n>is_standard_simd_type</c-><c- p>)</c->        <c- c1>// int, float, complex, etc.</c->
        <c- k>return</c-> <c- n>details</c-><c- o>::</c-><c- n>plus</c-><c- p>(</c-><c- n>lhs</c-><c- p>,</c-> <c- n>rhs</c-><c- p>);</c->
    <c- k>else</c-> <c- k>if</c-> <c- k>constexpr</c-> <c- p>(</c-><c- n>details</c-><c- o>::</c-><c- n>simd_has_custom_binary_plus</c-><c- p>)</c->  <c- c1>// user customisation point</c->
        <c- k>return</c-> <c- n>simd_binary_plus</c-><c- p>(</c-><c- n>lhs</c-><c- p>,</c-> <c- n>rhs</c-><c- p>);</c->
    <c- k>else</c->
        <c- k>return</c-> <c- n>details</c-><c- o>::</c-><c- n>element_wise_plus</c-><c- p>(</c-><c- n>lhs</c-><c- p>,</c-> <c- n>rhs</c-><c- p>);</c->     <c- c1>// Infer from scalar operator</c->
<c- p>}</c-></pre>
   <p>In this example <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> is only put in the overload set if a
builtin-arithmetic type supports addition directly or has a suitable
customisation point. Internally, the function will then invoke the appropriate
implementation.</p>
   <p>We need to specify what the customisation points will be called to allow them to
be discoverable. In the example above we have explicitly named the customisation
function <code class="highlight"><c- n>simd_binary_plus</c-></code>. This has the advantage that it is very clear and
unambiguous in what it does, but it does introduce potential for high levels of
duplication. This is because every operator will have its own unique
customisation point name.</p>
   <p>An alternative is to exploit the transparent templates that already exist in C++
and use them to differentiate between operations. Here is an example of the
signature of a customisation point for a user defined type:</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>typename</c-> <c- nc>Abi</c-><c- p>,</c-> <c- k>typename</c-> <c- nc>CustomType</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>invocable</c-><c- o>&lt;</c-><c- n>CustomType</c-><c- p>,</c-> <c- n>CustomType</c-><c- o>></c-> <c- n>Fn</c-><c- o>></c->
<c- k>constexpr</c-> <c- k>auto</c->
<c- n>simd_binary_op</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_simd</c-><c- o>&lt;</c-><c- n>CustomType</c-><c- p>,</c-> <c- n>Abi</c-><c- o>>&amp;</c-> <c- n>lhs</c-><c- p>,</c->
               <c- k>const</c-> <c- n>basic_simd</c-><c- o>&lt;</c-><c- n>CustomType</c-><c- p>,</c-> <c- n>Abi</c-><c- o>>&amp;</c-> <c- n>rhs</c-><c- p>,</c->
               <c- n>Fn</c-> <c- n>op</c-><c- p>);</c-></pre>
   <p>This has a unique and distinctive name to mark it as a customisation point, and as
a binary operator it takes in two <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>custom</c-><c- o>-</c-><c- n>type</c-><c- o>></c-></code> inputs. It also takes a
third parameter which specifies what binary operation to perform, chosen from
the list of standard template wrappers. For example, the call site in <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> would look like this:</p>
<pre class="highlight"><c- k>return</c-> <c- n>simd_binary_op</c-><c- p>(</c-><c- n>lhs</c-><c- p>,</c-> <c- n>rhs</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>plus</c-><c- o>&lt;></c-><c- p>{});</c-></pre>
   <p>Similarly, unary operators can be customized using a customization function
called <code class="highlight"><c- n>simd_unary_op</c-></code> which accept a unary transparent template wrapper.</p>
   <p>The advantage of using this mechanism rather than named functions for every
required operator is that it removes the need for many different functions, and
allows related operations to be consolidated into a single function. It also
allows the transparent operator itself to be invoked directly to perform an
operation. For example, suppose we want to define a customisation point for a
user defined type that has non-standard behaviour for multiply and divide, but
everything else works like a standard arithmetic operator (examples
of such types include complex numbers and fixed-point numbers). The following
pseudo-implementation captures this behaviour:</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>typename</c-> <c- nc>Abi</c-><c- p>,</c-> <c- k>typename</c-> <c- nc>CustomType</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>invocable</c-><c- o>&lt;</c-><c- n>CustomType</c-><c- p>,</c-> <c- n>CustomType</c-><c- o>></c-> <c- n>Fn</c-><c- o>></c->
<c- k>constexpr</c-> <c- k>auto</c->
<c- n>simd_binary_op</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_simd</c-><c- o>&lt;</c-><c- n>CustomType</c-><c- p>,</c-> <c- n>Abi</c-><c- o>>&amp;</c-> <c- n>lhs</c-><c- p>,</c->
               <c- k>const</c-> <c- n>basic_simd</c-><c- o>&lt;</c-><c- n>CustomType</c-><c- p>,</c-> <c- n>Abi</c-><c- o>>&amp;</c-> <c- n>rhs</c-><c- p>,</c->
               <c- n>Fn</c-> <c- n>op</c-><c- p>)</c->
<c- p>{</c->
  <c- c1>// Special case for some operators</c->
  <c- k>if</c->      <c- k>constexpr</c-> <c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>is_same_v</c-><c- o>&lt;</c-><c- n>Fn</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>multiplies</c-><c- o>&lt;>></c-><c- p>)</c-> <c- k>return</c-> <c- n>doCustomMultiply</c-><c- p>(</c-><c- n>lhs</c-><c- p>,</c-> <c- n>rhs</c-><c- p>);</c->
  <c- k>else</c-> <c- k>if</c-> <c- k>constexpr</c-> <c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>is_same_v</c-><c- o>&lt;</c-><c- n>Fn</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>divides</c-><c- o>&lt;>></c-><c- p>)</c->    <c- k>return</c-> <c- n>doCustomDivides</c-><c- p>(</c-><c- n>lhs</c-><c- p>,</c-> <c- n>rhs</c-><c- p>);</c->
  <c- c1>// All other cases defer to an integer instead.</c->
  <c- k>else</c-> <c- k>return</c-> <c- n>op</c-><c- p>(</c-><c- n>simd</c-><c- o>&lt;</c-><c- b>int</c-><c- o>></c-><c- p>(</c-><c- n>lhs</c-><c- p>),</c-> <c- n>simd</c-><c- o>&lt;</c-><c- b>int</c-><c- o>></c-><c- p>(</c-><c- n>rhs</c-><c- p>));</c->
<c- p>}</c-></pre>
   <p>This is not only less verbose but it also makes it obvious how and why the custom
type has to be handled differently to a builtin-type.</p>
   <p>Unfortunately shift operators don’t have transparent wrappers, so if we did use
this approach we need one of the following too:</p>
   <ul>
    <li data-md>
     <p>a specially named customization point (e.g., <code class="highlight"><c- n>simd_shift_left_op</c-></code>)</p>
    <li data-md>
     <p>an additional transparent operator added to <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> to allow the existing binary operation to be used (e.g., <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd_shift_left</c-><c- o>&lt;></c-></code>)</p>
    <li data-md>
     <p>a standardised <code class="highlight"><c- n>shift_left</c-></code> transparent operator (i.e., <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>shift_left</c-><c- o>&lt;></c-></code>)</p>
   </ul>
   <p>In the Intel example implementation we have used the second of these. Having a
different name and mechanism for shifts introduces extra complexity and
non-uniformity. We hope that a transparent operator wrapper for shift might be
added in future, in which case it will also be easier to transition to using
that if we provide a local alternative to begin with.</p>
   <h3 class="heading settled" data-level="3.4" id="conversion_customization"><span class="secno">3.4. </span><span class="content">Conversion customization</span><a class="self-link" href="#conversion_customization"></a></h3>
   <p>Conversions behave much like the customization points for arithmetic operators.
Like the other customisation operators conversions try three different
strategies:</p>
   <ul>
    <li data-md>
     <p>If a customisation for <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>UDT</c-><c- o>></c-></code> conversion exists, use that</p>
    <li data-md>
     <p>Otherwise if scalar conversion exists, invoke that element-wise</p>
    <li data-md>
     <p>Otherwise remove conversion capabilities</p>
   </ul>
   <p>These conversion rules will be used wherever needed with <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code>, not just within
the main constructor. For example, <code class="highlight"><c- n>copy_from</c-></code> will load data from memory into a <code class="highlight"><c- n>simd</c-></code> and then convert it to the desired output type.</p>
   <h3 class="heading settled" data-level="3.5" id="free_function_customization"><span class="secno">3.5. </span><span class="content">Overloads for free-functions</span><a class="self-link" href="#free_function_customization"></a></h3>
   <p>Anything outside <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> itself can be freely overloaded for the custom type. For
example, <code class="highlight"><c- n>abs</c-></code> could be provided as follows:</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>typename</c-> <c- nc>Abi</c-><c- o>></c->
<c- k>constexpr</c-> <c- k>auto</c-> <c- n>abs</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_simd</c-><c- o>&lt;</c-><c- n>fixed_point_16s8</c-><c- p>,</c-> <c- n>Abi</c-><c- o>>&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
    <c- k>return</c-> <c- d>/* special-abs-impl */</c-><c- p>;</c->
<c- p>}</c-></pre>
   <p>No <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code>-specific customization points are required for any of the other functions as overloads will suffice.</p>
   <h2 class="heading settled" data-level="4" id="extended_standard_support"><span class="secno">4. </span><span class="content">Extending support to <code class="highlight"><c- k>enum</c-></code> and <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>byte</c-></code></span><a class="self-link" href="#extended_standard_support"></a></h2>
   <p>It is useful to be able to create a <code class="highlight"><c- n>simd</c-></code> of enumerations and <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>byte</c-></code> and
the mechanisms defined in this proposal make this trivial to implement. However,
we would also define the following free-functions to mirror the support
available  with these standard types.</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>Enum</c-><c- p>,</c-> <c- k>typename</c-> <c- nc>Abi</c-><c- o>></c->
<c- k>constexpr</c-> <c- n>std</c-><c- o>::</c-><c- n>basic_simd</c-><c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- n>underlying_type_t</c-><c- o>&lt;</c-><c- n>Enum</c-><c- o>></c-><c- p>,</c-> <c- n>Abi</c-><c- o>></c->
  <c- n>to_underlying</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>basic_simd</c-><c- o>&lt;</c-><c- n>Enum</c-><c- p>,</c-> <c- n>Abi</c-><c- o>></c-> <c- n>se</c-><c- p>)</c-> <c- k>noexcept</c-><c- p>;</c-></pre>
<pre class="highlight"><c- k>template</c-> <c- o>&lt;</c-><c- k>class</c-> <c- nc>IntegerType</c-><c- p>,</c-> <c- k>typename</c-> <c- nc>Abi</c-><c- o>></c->
<c- k>constexpr</c-> <c- n>std</c-><c- o>::</c-><c- n>basic_simd</c-><c- o>&lt;</c-><c- n>IntegerType</c-><c- p>,</c-> <c- n>Abi</c-><c- o>></c->
  <c- n>to_integer</c-><c- p>(</c-> <c- n>std</c-><c- o>::</c-><c- n>basic_simd</c-><c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- n>byte</c-><c- p>,</c-> <c- n>Abi</c-><c- o>></c-> <c- n>b</c-> <c- p>)</c-> <c- k>noexcept</c-><c- p>;</c-></pre>
   <h2 class="heading settled" data-level="5" id="implementation_experience"><span class="secno">5. </span><span class="content">Implementation Experience</span><a class="self-link" href="#implementation_experience"></a></h2>
   <p>In Intel’s implementation of <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code>, the customization points described in
this proposal have been implemented so that we can use instantiations of <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> which use signal processing data types such as fixed-point or
saturating integrals. In the following example we show code from our
implementation which has been used to create a <code class="highlight"><c- n>simd</c-></code> of a user defined
saturating data type. We start by showing how the compiler does a reasonable job
of inferring the required operators, but that certain operators prove too
difficult to do well, at which point we can provide customisation points to
smooth things out. We expect that a similar process will be used when other
user-defined types are implemented.</p>
   <p>Consider what might be needed to make a saturating data type. To begin with, we might already have a 16-bit scalar saturating data type:</p>
<pre class="highlight"><c- k>struct</c-> <c- nc>saturating_int16</c->
<c- p>{</c->
    <c- n>saturating_int16</c-><c- p>(</c-><c- b>int</c-> <c- n>v</c-><c- p>)</c-> <c- o>:</c-> <c- n>data</c-><c- p>(</c-><c- n>v</c-><c- p>)</c-> <c- p>{}</c->
    <c- b>int16_t</c-> <c- n>data</c-><c- p>;</c->

    <c- c1>// Addition</c->
    <c- k>friend</c-> <c- n>saturating_int16</c-> <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>saturating_int16</c-> <c- n>lhs</c-><c- p>,</c->
                                      <c- n>saturating_int16</c-> <c- n>rhs</c-><c- p>)</c-> <c- p>{</c->
        <c- k>auto</c-> <c- n>r</c-> <c- o>=</c-> <c- b>int32_t</c-><c- p>(</c-><c- n>lhs</c-><c- p>.</c-><c- n>data</c-><c- p>)</c-> <c- o>+</c-> <c- b>int32_t</c-><c- p>(</c-><c- n>rhs</c-><c- p>.</c-><c- n>data</c-><c- p>);</c->
        <c- k>return</c-> <c- b>int16_t</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>min</c-><c- o>&lt;</c-><c- b>int32_t</c-><c- o>></c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>max</c-><c- o>&lt;</c-><c- b>int32_t</c-><c- o>></c-><c- p>(</c-><c- n>r</c-><c- p>,</c-> <c- mi>-32768</c-><c- p>),</c-> <c- mi>32767</c-><c- p>));</c->
    <c- p>}</c->

    <c- k>friend</c-> <c- b>bool</c-> <c- k>operator</c-><c- o>></c-><c- p>(</c-><c- n>saturating_int16</c-> <c- n>lhs</c-><c- p>,</c->
                          <c- n>saturating_int16</c-> <c- n>rhs</c-><c- p>)</c-> <c- p>{</c->
      <c- k>return</c-> <c- n>lhs</c-><c- p>.</c-><c- n>data</c-> <c- o>></c-> <c- n>rhs</c-><c- p>.</c-><c- n>data</c-><c- p>;</c->
    <c- p>}</c->

    <c- c1>// Other operators also defined, but omitted for brevity.</c->
<c- p>};</c-></pre>
   <p>Let us begin with simple permute operations which illustrates that storage and
bit-copy operations will work as expected:</p>
   <table>
    <thead>
     <tr>
      <th>C++ Code
      <th>Assembly
    <tbody>
     <tr>
      <td>
<pre class="highlight"><c- k>auto</c-> <c- n>broadcast</c-><c- p>(</c-><c- b>int16_t</c-> <c- n>x</c-><c- p>)</c->
<c- p>{</c->
  <c- k>return</c-> <c- n>simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- o>></c-><c- p>(</c-><c- n>x</c-><c- p>);</c->
<c- p>}</c-></pre>
      <td>
<pre class="highlight"><c- n>broadcast</c-><c- p>(</c-><c- b>short</c-><c- p>)</c-><c- o>:</c->
        <c- n>vpbroadcastw</c->    <c- n>zmm0</c-><c- p>,</c-> <c- n>edi</c->
        <c- n>ret</c-></pre>
     <tr>
      <td>
<pre class="highlight"><c- k>auto</c-> <c- n>iq_swap</c-><c- p>(</c-><c- k>const</c-> <c- n>simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- o>>&amp;</c-> <c- n>v</c-><c- p>)</c->
<c- p>{</c->
  <c- k>return</c-> <c- n>permute</c-><c- p>(</c-><c- n>v</c-><c- p>,</c-> <c- p>[](</c-><c- k>auto</c-> <c- n>idx</c-><c- p>)</c-> <c- p>{</c->
    <c- k>return</c-> <c- n>idx</c-> <c- o>^</c-> <c- mi>1</c-><c- p>;</c->
  <c- p>});</c->
<c- p>}</c-></pre>
      <td>
<pre class="highlight"><c- n>iq_swap</c-><c- p>([...]</c-><c- o>></c-> <c- k>const</c-><c- o>&amp;</c-><c- p>)</c-><c- o>:</c-> #
       <c- n>vprold</c->  <c- n>zmm0</c-><c- p>,</c-> <c- n>zmmword</c-> <c- n>ptr</c-> <c- p>[</c-><c- n>rdi</c-><c- p>],</c-> <c- mi>16</c->
       <c- n>ret</c-></pre>
   </table>
   <p>Our original type defined addition, so we should be able to automatically call
addition and its derivatives for the corresponding user-defined type:</p>
   <table>
    <thead>
     <tr>
      <th>C++ Code
      <th>Assembly
    <tbody>
     <tr>
      <td>
<pre class="highlight"><c- k>auto</c-> <c- n>add</c-><c- p>(</c-><c- n>simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- o>></c-> <c- n>lhs</c-><c- p>,</c->
         <c- n>simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- o>></c-> <c- n>rhs</c-><c- p>)</c->
<c- p>{</c->
    <c- k>return</c-> <c- n>lhs</c-> <c- o>+</c-> <c- n>rhs</c-><c- p>;</c->
<c- p>}</c-></pre>
      <td>
<pre class="highlight"><c- n>add</c-><c- p>([...])</c-><c- o>:</c-> #
        <c- n>vpaddsw</c-> <c- n>ymm0</c-><c- p>,</c-> <c- n>ymm0</c-><c- p>,</c-> <c- n>ymm1</c->
        <c- n>ret</c-></pre>
     <tr>
      <td>
<pre class="highlight"><c- k>auto</c-> <c- n>compound_add</c-><c- p>(</c-><c- n>simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- o>></c-> <c- n>lhs</c-><c- p>,</c->
                  <c- n>simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- o>></c-> <c- n>rhs</c-><c- p>)</c->
<c- p>{</c->
    <c- n>lhs</c-> <c- o>+=</c-> <c- n>rhs</c-><c- p>;</c->
    <c- k>return</c-> <c- n>lhs</c-><c- p>;</c->
<c- p>}</c-></pre>
      <td>
<pre class="highlight"><c- n>compound_add</c-><c- p>([...])</c-><c- o>:</c-> #
        <c- n>vpaddsw</c-> <c- n>ymm0</c-><c- p>,</c-> <c- n>ymm0</c-><c- p>,</c-> <c- n>ymm1</c->
        <c- n>ret</c-></pre>
   </table>
   <p>Note that the compiler (Intel oneAPI 2024.0) has done an excellent job, even
generating a real saturation instruction. Unfortunately there is a small issue
with the quality of the generated code, which we shall return to shortly.</p>
   <p>Next, we need to be able to compare saturated values. Our original class was
able to do this using its own operator, allowing us to unlock comparisons
including reduction comparisons.</p>
   <table>
    <thead>
     <tr>
      <th>C++ Code
      <th>Assembly
    <tbody>
     <tr>
      <td>
<pre class="highlight"><c- k>auto</c-> <c- n>cmp_lt</c-><c- p>(</c-><c- n>simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- o>></c-> <c- n>lhs</c-><c- p>,</c->
            <c- n>simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- o>></c-> <c- n>rhs</c-><c- p>)</c->
<c- p>{</c->
    <c- k>return</c-> <c- n>lhs</c-> <c- o>></c-> <c- n>rhs</c-><c- p>;</c->
<c- p>}</c-></pre>
      <td>
<pre class="highlight"><c- n>cmp_lt</c-><c- p>([...])</c-><c- o>:</c-> #
        <c- n>vpcmpgtw</c->        <c- n>ymm0</c-><c- p>,</c-> <c- n>ymm1</c-><c- p>,</c-> <c- n>ymm0</c->
        <c- n>ret</c-></pre>
     <tr>
      <td>
<pre class="highlight"><c- k>auto</c-> <c- n>biggest</c-><c- p>(</c-><c- n>simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- o>></c-> <c- n>lhs</c-><c- p>,</c->
             <c- n>simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- o>></c-> <c- n>rhs</c-><c- p>)</c->
<c- p>{</c->
    <c- k>return</c-> <c- n>max</c-><c- p>(</c-><c- n>lhs</c-><c- p>,</c-> <c- n>rhs</c-><c- p>);</c->
<c- p>}</c-></pre>
      <td>
<pre class="highlight"><c- n>biggest</c-><c- p>([...])</c-><c- o>:</c-> #
        <c- n>vpmaxsw</c-> <c- n>ymm0</c-><c- p>,</c-> <c- n>ymm0</c-><c- p>,</c-> <c- n>ymm1</c->
        <c- n>ret</c-></pre>
   </table>
   <p>Although the compiler has done a good job of the examples above, there were
unfortunately a few places where it didn’t do so well. For example:</p>
   <table>
    <thead>
     <tr>
      <th>C++ Code
      <th>Assembly
    <tbody>
     <tr>
      <td>
<pre class="highlight"><c- k>auto</c-> <c- n>reduce_add</c-><c- p>(</c-><c- n>simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- o>></c-> <c- n>v</c-><c- p>)</c->
<c- p>{</c->
    <c- k>return</c-> <c- n>reduce</c-><c- p>(</c-><c- n>v</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>plus</c-><c- o>&lt;></c-><c- p>{});</c->
<c- p>}</c-></pre>
      <td>
<pre class="highlight"><c- n>reduce_add</c-><c- p>([...])</c-><c- o>:</c->
        <c- n>vextracti128</c->    <c- n>xmm1</c-><c- p>,</c-> <c- n>ymm0</c-><c- p>,</c-> <c- mi>1</c->
        <c- n>vpaddsw</c-> <c- n>xmm0</c-><c- p>,</c-> <c- n>xmm0</c-><c- p>,</c-> <c- n>xmm1</c->
        <c- n>vpextrq</c-> <c- n>rdx</c-><c- p>,</c-> <c- n>xmm0</c-><c- p>,</c-> <c- mi>1</c->
        <c- n>vmovq</c->   <c- n>rax</c-><c- p>,</c-> <c- n>xmm0</c->
        <c- n>mov</c->     <c- n>rsi</c-><c- p>,</c-> <c- n>rax</c->
        <c- n>shr</c->     <c- n>rsi</c-><c- p>,</c-> <c- mi>48</c->
        <c- n>mov</c->     <c- n>rcx</c-><c- p>,</c-> <c- n>rdx</c->
        <c- n>shr</c->     <c- n>rcx</c-><c- p>,</c-> <c- mi>48</c->
        <c- n>lea</c->     <c- n>edi</c-><c- p>,</c-> <c- p>[</c-><c- n>rsi</c-> <c- o>+</c-> <c- n>rcx</c-><c- p>]</c->
        <c- n>movsx</c->   <c- n>edi</c-><c- p>,</c-> <c- n>di</c->
        <c- n>sar</c->     <c- n>edi</c-><c- p>,</c-> <c- mi>15</c->
        <c- n>xor</c->     <c- n>edi</c-><c- p>,</c-> <c- mi>-32768</c->
        <c- n>add</c->     <c- n>si</c-><c- p>,</c-> <c- n>cx</c->
        <c- n>cmovo</c->   <c- n>esi</c-><c- p>,</c-> <c- n>edi</c->
        <c- p>...</c-></pre>
   </table>
   <p>For an unknown reason the compiler has switched to using element-by-element
application of the scalar operation here, which is considerably slower. It is
likely that we will fix the compiler to correct whatever mistake it is making
here, but for now we can use the customisation point mechanism to aid the
compiler in doing a better job. We want to change the behavior of <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> so
that addition is handled explicitly using the known intrinsics. We do this by
defining our customisation function. Here is a very simple implementation which
runs on an Intel AVX2 machine. It could be extended to work on all Intel
instruction sets (e.g, AVX-512) but that is outside the scope of this proposal.</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>typename</c-> <c- nc>Abi</c-><c- o>></c->
<c- k>constexpr</c-> <c- k>auto</c-> <c- n>simd_binary_op</c-><c- p>(</c-><c- k>const</c-> <c- n>xvec</c-><c- o>::</c-><c- n>basic_simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- p>,</c-> <c- n>Abi</c-><c- o>>&amp;</c-> <c- n>lhs</c-><c- p>,</c->
                              <c- k>const</c-> <c- n>xvec</c-><c- o>::</c-><c- n>basic_simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- p>,</c-> <c- n>Abi</c-><c- o>>&amp;</c-> <c- n>rhs</c-><c- p>,</c->
                              <c- n>std</c-><c- o>::</c-><c- n>plus</c-><c- o>&lt;></c-><c- p>)</c->
<c- p>{</c->
    <c- k>constexpr</c-> <c- b>int</c-> <c- n>numElements</c-> <c- o>=</c-> <c- n>xvec</c-><c- o>::</c-><c- n>basic_simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- p>,</c-> <c- n>Abi</c-><c- o>>::</c-><c- n>size</c-><c- p>;</c->
    <c- k>if</c-> <c- k>constexpr</c-> <c- p>(</c-><c- n>numElements</c-> <c- o>&lt;=</c-> <c- mi>8</c-><c- p>)</c->
        <c- k>return</c-> <c- n>basic_simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- p>,</c-> <c- n>Abi</c-><c- o>></c-><c- p>(</c->
                 <c- n>_mm_adds_epi16</c-><c- p>(</c-><c- n>lhs</c-><c- p>.</c-><c- n>to_register</c-><c- p>(),</c-> <c- n>rhs</c-><c- p>.</c-><c- n>to_register</c-><c- p>()));</c->
    <c- k>else</c-> 
        <c- k>return</c-> <c- n>basic_simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- p>,</c-> <c- n>Abi</c-><c- o>></c-><c- p>(</c->
                <c- n>_mm256_adds_epi16</c-><c- p>(</c-><c- n>lhs</c-><c- p>.</c-><c- n>to_register</c-><c- p>(),</c-> <c- n>rhs</c-><c- p>.</c-><c- n>to_register</c-><c- p>()));</c->
<c- p>}</c-></pre>
   <p>This now generates code like this:</p>
   <table>
    <thead>
     <tr>
      <th>C++ Code
      <th>Assembly
    <tbody>
     <tr>
      <td>
<pre class="highlight"><c- k>auto</c-> <c- n>reduce_add</c-><c- p>(</c-><c- n>simd</c-><c- o>&lt;</c-><c- n>saturating_int16</c-><c- o>></c-> <c- n>v</c-><c- p>)</c->
<c- p>{</c->
    <c- k>return</c-> <c- n>reduce</c-><c- p>(</c-><c- n>v</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>plus</c-><c- o>&lt;></c-><c- p>{});</c->
<c- p>}</c-></pre>
      <td>
<pre class="highlight"><c- n>reduce_add</c-><c- p>([...])</c-><c- o>:</c->
        <c- n>vextracti128</c->    <c- n>xmm1</c-><c- p>,</c-> <c- n>ymm0</c-><c- p>,</c-> <c- mi>1</c->
        <c- n>vpaddsw</c-> <c- n>xmm0</c-><c- p>,</c-> <c- n>xmm0</c-><c- p>,</c-> <c- n>xmm1</c->
        <c- n>vpshufd</c-> <c- n>xmm1</c-><c- p>,</c-> <c- n>xmm0</c-><c- p>,</c-> <c- mi>238</c->
        <c- n>vpaddsw</c-> <c- n>xmm0</c-><c- p>,</c-> <c- n>xmm0</c-><c- p>,</c-> <c- n>xmm1</c->
        <c- n>vmovq</c->   <c- n>rax</c-><c- p>,</c-> <c- n>xmm0</c->
        <c- n>vmovd</c->   <c- n>xmm0</c-><c- p>,</c-> <c- n>eax</c->
        <c- n>shr</c->     <c- n>rax</c-><c- p>,</c-> <c- mi>32</c->
        <c- n>vmovd</c->   <c- n>xmm1</c-><c- p>,</c-> <c- n>eax</c->
        <c- n>vpaddsw</c-> <c- n>xmm0</c-><c- p>,</c-> <c- n>xmm0</c-><c- p>,</c-> <c- n>xmm1</c->
        <c- n>vmovd</c->   <c- n>eax</c-><c- p>,</c-> <c- n>xmm0</c->
        <c- n>shr</c->     <c- n>eax</c-><c- p>,</c-> <c- mi>16</c->
        <c- n>vmovd</c->   <c- n>xmm1</c-><c- p>,</c-> <c- n>eax</c->
        <c- n>vpaddsw</c-> <c- n>xmm0</c-><c- p>,</c-> <c- n>xmm0</c-><c- p>,</c-> <c- n>xmm1</c->
        <c- n>vmovd</c->   <c- n>eax</c-><c- p>,</c-> <c- n>xmm0</c->
        <c- n>vzeroupper</c->
        <c- n>ret</c-></pre>
   </table>
   <p>This is acceptably better code.</p>
   <h3 class="heading settled" data-level="5.1" id="summary_implementation_experience"><span class="secno">5.1. </span><span class="content">Summary of implementation experience</span><a class="self-link" href="#summary_implementation_experience"></a></h3>
   <p>In this section we have given a simple example in which we started with a scalar
user-defined type and automatically inferred the <code class="highlight"><c- n>simd</c-></code> versions of its
operators, allowing us to quickly develop code which used <code class="highlight"><c- n>simd</c-><c- o>&lt;</c-><c- n>UDT</c-><c- o>></c-></code>. During
inspection of the generated assembly code we found an issue with the quality of
the code which we were able to overcome by using a customisation point to help
guide the compiler to generate the correct code.</p>
   <p>The complete finished example could be used in real code, and allow the user to
quickly extend their own type to something that can be made to work with all of
the <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> APIs, with performant quality code, with relatively minimal
effort.</p>
   <h2 class="heading settled" data-level="6" id="future_work"><span class="secno">6. </span><span class="content">Future work</span><a class="self-link" href="#future_work"></a></h2>
   <p>SG6 suggested that all operators should be able to take different input and
outputs to facilitate adding <code class="highlight"><c- n>mp</c-><c- o>-</c-><c- n>uints</c-></code>, <code class="highlight"><c- n>constrained_numbers</c-></code>, and other
mixed-type operators. Although this work is incomplete it is still useful to get LEWG
feedback on the direction of the other features.</p>
   <h2 class="heading settled" data-level="7" id="conclusion"><span class="secno">7. </span><span class="content">Conclusion</span><a class="self-link" href="#conclusion"></a></h2>
   <p>In this proposal we have outlined the basic mechanisms needed to allow
user-defined types to be stored and manipulated by <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> values, and
crucially, to be able to do so without knowledge of the internal implementation
of the <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>simd</c-></code> library.</p>
   <h2 class="heading settled" data-level="8" id="acknowledgements"><span class="secno">8. </span><span class="content">Acknowledgements</span><a class="self-link" href="#acknowledgements"></a></h2>
   <p>We would like to thank Matthias Kretz for his feedback and his useful contributions to discussions.</p>
   <h2 class="heading settled" data-level="9" id="revision_history"><span class="secno">9. </span><span class="content">Revision History</span><a class="self-link" href="#revision_history"></a></h2>
   <p>R0 => R1</p>
   <ul>
    <li data-md>
     <p>Incorporated SG1 and SG6 feedback from 2024 Tokyo meeting.</p>
    <li data-md>
     <p>Added restrictions on element types (e.g., size).</p>
    <li data-md>
     <p>Added inferencing as a valid method for constructing <code class="highlight"><c- n>simd</c-></code> operators from scalar operators.</p>
    <li data-md>
     <p>Added type conversions.</p>
    <li data-md>
     <p>Removed opt-in and replaced with opt-out.</p>
    <li data-md>
     <p>Removed explicit user-defined storage.</p>
    <li data-md>
     <p>Provided inferencing example.</p>
   </ul>
  </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-p1928r8">[P1928R8]
   <dd>Matthias Kretz. <a href="https://wg21.link/p1928r8"><cite>std::simd - Merge data-parallel types from the Parallelism TS 2</cite></a>. 9 November 2023. URL: <a href="https://wg21.link/p1928r8">https://wg21.link/p1928r8</a>
   <dt id="biblio-p2663r4">[P2663R4]
   <dd>Daniel Towner, Ruslan Arutyunyan. <a href="https://wg21.link/p2663r4"><cite>Proposal to support interleaved complex values in std::simd</cite></a>. 13 October 2023. URL: <a href="https://wg21.link/p2663r4">https://wg21.link/p2663r4</a>
  </dl>