<!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>P2591R4: Concatenation of strings and string views</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 5edf5e459, updated Thu Jun 22 11:28:01 2023 -0700" name="generator">
  <link href="https://isocpp.org/favicon.ico" rel="icon">
<style>/* Boilerplate: style-autolinks */
.css.css, .property.property, .descriptor.descriptor {
    color: var(--a-normal-text);
    font-size: inherit;
    font-family: inherit;
}
.css::before, .property::before, .descriptor::before {
    content: "‘";
}
.css::after, .property::after, .descriptor::after {
    content: "’";
}
.property, .descriptor {
    /* Don't wrap property and descriptor names */
    white-space: nowrap;
}
.type { /* CSS value <type> */
    font-style: italic;
}
pre .property::before, pre .property::after {
    content: "";
}
[data-link-type="property"]::before,
[data-link-type="propdesc"]::before,
[data-link-type="descriptor"]::before,
[data-link-type="value"]::before,
[data-link-type="function"]::before,
[data-link-type="at-rule"]::before,
[data-link-type="selector"]::before,
[data-link-type="maybe"]::before {
    content: "‘";
}
[data-link-type="property"]::after,
[data-link-type="propdesc"]::after,
[data-link-type="descriptor"]::after,
[data-link-type="value"]::after,
[data-link-type="function"]::after,
[data-link-type="at-rule"]::after,
[data-link-type="selector"]::after,
[data-link-type="maybe"]::after {
    content: "’";
}

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

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

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

@media (prefers-color-scheme: dark) {
    :root {
        --selflink-text: black;
        --selflink-bg: silver;
        --selflink-hover-text: white;
    }
}
</style>
<style>/* Boilerplate: style-colors */

/* Any --*-text not paired with a --*-bg is assumed to have a transparent bg */
:root {
    color-scheme: light dark;

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

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

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

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

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

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

    --heading-text: #005a9c;

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

    --algo-border: #def;

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

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

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

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

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

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

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

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

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

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

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

    --datacell-border: silver;

    --indexinfo-text: #707070;

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

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

    --editedrec-bg: darkorange;
}

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

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

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

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

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

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

        --heading-text: #8af;

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

        --algo-border: #456;

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

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

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

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

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

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

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

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

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

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

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

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

        --datacell-border: silver;

        --indexinfo-text: #aaa;

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

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

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

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

figcaption {
    counter-increment: figure;
}
figcaption:not(.no-marker)::before {
    content: "Figure " counter(figure) " ";
}
</style>
<style>/* Boilerplate: style-issues */
a[href].issue-return {
    float: right;
    float: inline-end;
    color: var(--issueheading-text);
    font-weight: bold;
    text-decoration: none;
}
</style>
<style>/* Boilerplate: style-md-lists */
/* This is a weird hack for me not yet following the commonmark spec
   regarding paragraph and lists. */
[data-md] > :first-child {
    margin-top: 0;
}
[data-md] > :last-child {
    margin-bottom: 0;
}
</style>
<style>/* Boilerplate: style-selflinks */

:root {
    --selflink-text: white;
    --selflink-bg: gray;
    --selflink-hover-text: black;
}
.heading, .issue, .note, .example, li, dt {
    position: relative;
}
a.self-link {
    position: absolute;
    top: 0;
    left: calc(-1 * (3.5rem - 26px));
    width: calc(3.5rem - 26px);
    height: 2em;
    text-align: center;
    border: none;
    transition: opacity .2s;
    opacity: .5;
}
a.self-link:hover {
    opacity: 1;
}
.heading > a.self-link {
    font-size: 83%;
}
.example > a.self-link,
.note > a.self-link,
.issue > a.self-link {
    /* These blocks are overflow:auto, so positioning outside
       doesn't work. */
    left: auto;
    right: 0;
}
li > a.self-link {
    left: calc(-1 * (3.5rem - 26px) - 2em);
}
dfn > a.self-link {
    top: auto;
    left: auto;
    opacity: 0;
    width: 1.5em;
    height: 1.5em;
    background: var(--selflink-bg);
    color: var(--selflink-text);
    font-style: normal;
    transition: opacity .2s, background-color .2s, color .2s;
}
dfn:hover > a.self-link {
    opacity: 1;
}
dfn > a.self-link:hover {
    color: var(--selflink-hover-text);
}

a.self-link::before            { content: "¶"; }
.heading > a.self-link::before { content: "§"; }
dfn > a.self-link::before      { content: "#"; }
</style>
<style>/* Boilerplate: style-syntax-highlighting */

code.highlight { padding: .1em; border-radius: .3em; }
pre.highlight, pre > code.highlight { display: block; padding: 1em; margin: .5em 0; overflow: auto; border-radius: 0; }

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


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

    c-[a] { color: #d33682 } /* Keyword.Declaration */
    c-[b] { color: #d33682 } /* Keyword.Type */
    c-[c] { color: #2aa198 } /* Comment */
    c-[d] { color: #2aa198 } /* Comment.Multiline */
    c-[e] { color: #268bd2 } /* Name.Attribute */
    c-[f] { color: #b58900 } /* Name.Tag */
    c-[g] { color: #cb4b16 } /* Name.Variable */
    c-[k] { color: #d33682 } /* Keyword */
    c-[l] { color: #657b83 } /* Literal */
    c-[m] { color: #657b83 } /* Literal.Number */
    c-[n] { color: #268bd2 } /* Name */
    c-[o] { color: #657b83 } /* Operator */
    c-[p] { color: #657b83 } /* Punctuation */
    c-[s] { color: #6c71c4 } /* Literal.String */
    c-[t] { color: #6c71c4 } /* Literal.String.Single */
    c-[u] { color: #6c71c4 } /* Literal.String.Double */
    c-[ch] { color: #2aa198 } /* Comment.Hashbang */
    c-[cp] { color: #2aa198 } /* Comment.Preproc */
    c-[cpf] { color: #2aa198 } /* Comment.PreprocFile */
    c-[c1] { color: #2aa198 } /* Comment.Single */
    c-[cs] { color: #2aa198 } /* Comment.Special */
    c-[kc] { color: #d33682 } /* Keyword.Constant */
    c-[kn] { color: #d33682 } /* Keyword.Namespace */
    c-[kp] { color: #d33682 } /* Keyword.Pseudo */
    c-[kr] { color: #d33682 } /* Keyword.Reserved */
    c-[ld] { color: #657b83 } /* Literal.Date */
    c-[nc] { color: #268bd2 } /* Name.Class */
    c-[no] { color: #268bd2 } /* Name.Constant */
    c-[nd] { color: #268bd2 } /* Name.Decorator */
    c-[ni] { color: #268bd2 } /* Name.Entity */
    c-[ne] { color: #268bd2 } /* Name.Exception */
    c-[nf] { color: #268bd2 } /* Name.Function */
    c-[nl] { color: #268bd2 } /* Name.Label */
    c-[nn] { color: #268bd2 } /* Name.Namespace */
    c-[py] { color: #268bd2 } /* Name.Property */
    c-[ow] { color: #657b83 } /* Operator.Word */
    c-[mb] { color: #657b83 } /* Literal.Number.Bin */
    c-[mf] { color: #657b83 } /* Literal.Number.Float */
    c-[mh] { color: #657b83 } /* Literal.Number.Hex */
    c-[mi] { color: #657b83 } /* Literal.Number.Integer */
    c-[mo] { color: #657b83 } /* Literal.Number.Oct */
    c-[sa] { color: #6c71c4 } /* Literal.String.Affix */
    c-[sb] { color: #6c71c4 } /* Literal.String.Backtick */
    c-[sc] { color: #6c71c4 } /* Literal.String.Char */
    c-[dl] { color: #6c71c4 } /* Literal.String.Delimiter */
    c-[sd] { color: #6c71c4 } /* Literal.String.Doc */
    c-[se] { color: #6c71c4 } /* Literal.String.Escape */
    c-[sh] { color: #6c71c4 } /* Literal.String.Heredoc */
    c-[si] { color: #6c71c4 } /* Literal.String.Interpol */
    c-[sx] { color: #6c71c4 } /* Literal.String.Other */
    c-[sr] { color: #6c71c4 } /* Literal.String.Regex */
    c-[ss] { color: #6c71c4 } /* Literal.String.Symbol */
    c-[fm] { color: #268bd2 } /* Name.Function.Magic */
    c-[vc] { color: #cb4b16 } /* Name.Variable.Class */
    c-[vg] { color: #cb4b16 } /* Name.Variable.Global */
    c-[vi] { color: #cb4b16 } /* Name.Variable.Instance */
    c-[vm] { color: #cb4b16 } /* Name.Variable.Magic */
    c-[il] { color: #657b83 } /* Literal.Number.Integer.Long */
}
</style>
 <body class="h-entry">
  <div class="head">
   <p data-fill-with="logo"></p>
   <h1 class="p-name no-ref" id="title">P2591R4<br>Concatenation of strings and string views</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="2023-06-08">2023-06-08</time></span></h2>
   <div data-fill-with="spec-metadata">
    <dl>
     <dt class="editor">Author:
     <dd class="editor p-author h-card vcard"><a class="p-name fn u-email email" href="mailto:giuseppe.dangelo@kdab.com">Giuseppe D'Angelo</a>
     <dt>Audience:
     <dd>LEWG
     <dt>Project:
     <dd>ISO/IEC JTC1/SC22/WG21 14882: Programming Language — C++
    </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>
    We propose to add overloads of 
    <tt>operator+</tt>
     between string

    and string view classes.
   </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="#changelog"><span class="secno">1</span> <span class="content">Changelog</span></a>
    <li>
     <a href="#motivation"><span class="secno">2</span> <span class="content">Motivation and Scope</span></a>
     <ol class="toc">
      <li><a href="#why-are-those-overloads-missing-in-the-first-place"><span class="secno">2.1</span> <span class="content">Why are those overloads missing in the first place?</span></a>
     </ol>
    <li><a href="#impact"><span class="secno">3</span> <span class="content">Impact On The Standard</span></a>
    <li>
     <a href="#designdecisions"><span class="secno">4</span> <span class="content">Design Decisions</span></a>
     <ol class="toc">
      <li><a href="#minimizingallocations"><span class="secno">4.1</span> <span class="content">Minimizing the number of allocations</span></a>
      <li>
       <a href="#hiddenfriends"><span class="secno">4.2</span> <span class="content">Should the proposed operators be hidden friends? Should they be function templates?</span></a>
       <ol class="toc">
        <li><a href="#approach-1-free-non-friend-function-templates-taking-exactly-a-string-view"><span class="secno">4.2.1</span> <span class="content">Approach 1: free non-friend function templates, taking exactly a string view</span></a>
        <li><a href="#approach-2-free-non-friend-function-templates-taking-anything-convertible-to-a-string-view"><span class="secno">4.2.2</span> <span class="content">Approach 2: free non-friend function templates, taking anything convertible to a string view</span></a>
        <li><a href="#approach-3-hidden-friends-non-template-functions"><span class="secno">4.2.3</span> <span class="content">Approach 3: hidden friends, non-template functions</span></a>
        <li><a href="#approach-4-hidden-friends-function-templates-taking-anything-convertible-to-a-string-view"><span class="secno">4.2.4</span> <span class="content">Approach 4: hidden friends, function templates, taking anything convertible to a string view</span></a>
        <li><a href="#summary"><span class="secno">4.2.5</span> <span class="content">Summary</span></a>
        <li><a href="#which-strategy-should-be-used"><span class="secno">4.2.6</span> <span class="content">Which strategy should be used?</span></a>
       </ol>
      <li><a href="#backwards-compatibility-and-annex-c"><span class="secno">4.3</span> <span class="content">Backwards compatibility and Annex C</span></a>
     </ol>
    <li><a href="#implementationexperience"><span class="secno">5</span> <span class="content">Implementation experience</span></a>
    <li>
     <a href="#technicalspecifications"><span class="secno">6</span> <span class="content">Technical Specifications</span></a>
     <ol class="toc">
      <li><a href="#feature-testing-macro"><span class="secno">6.1</span> <span class="content">Feature testing macro</span></a>
      <li><a href="#proposedwording"><span class="secno">6.2</span> <span class="content">Proposed wording</span></a>
     </ol>
    <li><a href="#acknowledgements"><span class="secno">7</span> <span class="content">Acknowledgements</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="changelog"><span class="secno">1. </span><span class="content">Changelog</span><a class="self-link" href="#changelog"></a></h2>
   <ul>
    <li data-md>
     <p>R4</p>
     <ul>
      <li data-md>
       <p>Incorporated feedback from the 2023-04-18 LEWG telecon.</p>
      <li data-md>
       <p>Reverted to a free function template implementation (Approach 2, as described below), following the poll(s) that showed no longer a consensus for using hidden friends and thus introducing an asymmetry.</p>
      <li data-md>
       <p>Added the requested tests (involving <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>filesystem</c-><c- o>::</c-><c- n>path</c-></code> and a test producing ambiguities) to the <a data-link-type="biblio" href="#biblio-p2591-gcc" title="P2591 prototype implementation for libstdc++">working prototype</a>.</p>
      <li data-md>
       <p>Rebased the wording on top of the latest draft.</p>
     </ul>
    <li data-md>
     <p>R3</p>
     <ul>
      <li data-md>
       <p>Reading improvements.</p>
     </ul>
    <li data-md>
     <p>R2</p>
     <ul>
      <li data-md>
       <p>Changed the signatures of the proposed operators to take precisely string views (instead of anything convertible to string views).</p>
      <li data-md>
       <p>Made the proposed operators hidden friends, following a LEWG vote in a telecon.</p>
      <li data-md>
       <p>Added a discussion about the implications of such a change.</p>
      <li data-md>
       <p>Added an Annex C entry.</p>
     </ul>
    <li data-md>
     <p>R1</p>
     <ul>
      <li data-md>
       <p>Minor reading improvements.</p>
     </ul>
    <li data-md>
     <p>R0</p>
     <ul>
      <li data-md>
       <p>First submission.</p>
     </ul>
   </ul>
   <h2 class="heading settled" data-level="2" id="motivation"><span class="secno">2. </span><span class="content">Motivation and Scope</span><a class="self-link" href="#motivation"></a></h2>
   <p>
    The Standard is currently lacking support for concatenating strings
and string views by means of 
    <tt>operator+</tt>
    :
   </p>
<pre class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- nf>calculate</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>string_view</c-> <c- n>prefix</c-><c- p>)</c->
<c- p>{</c->
  <c- k>return</c-> <c- n>prefix</c-> <c- o>+</c-> <c- n>get_string</c-><c- p>();</c-> <c- c1>// ERROR</c->
<c- p>}</c->
</pre>
   <p>This constitutes a major asymmetry when considering the rest of <code class="highlight"><c- n>basic_string</c-></code>'s API related to string concatenation. In such APIs
there is already support for the corresponding view classes.</p>
   <p>In general, this makes the concatenation APIs between string and
string views have a poor usability experience:</p>
<pre class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>str</c-><c- p>;</c->
<c- n>std</c-><c- o>::</c-><c- n>string_view</c-> <c- n>view</c-><c- p>;</c->

<c- c1>// Appending</c->
<c- n>str</c-> <c- o>+</c-> <c- n>view</c-><c- p>;</c->              <c- c1>// ERROR</c->
<c- n>str</c-> <c- o>+</c-> <c- n>std</c-><c- o>::</c-><c- n>string</c-><c- p>(</c-><c- n>view</c-><c- p>);</c-> <c- c1>// OK, but inefficient</c->
<c- n>str</c-> <c- o>+</c-> <c- n>view</c-><c- p>.</c-><c- n>data</c-><c- p>();</c->       <c- c1>// Compiles, but BUG!</c->

<c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>copy</c-> <c- o>=</c-> <c- n>str</c-><c- p>;</c->
<c- n>copy</c-> <c- o>+=</c-> <c- n>view</c-><c- p>;</c->            <c- c1>// OK, but tedious to write (requires explicit copy)</c->
<c- n>copy</c-><c- p>.</c-><c- n>append</c-><c- p>(</c-><c- n>view</c-><c- p>);</c->       <c- c1>// OK, ditto</c->


<c- c1>// Prepending</c->
<c- n>view</c-> <c- o>+</c-> <c- n>str</c-><c- p>;</c->              <c- c1>// ERROR</c->

<c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>copy</c-> <c- o>=</c-> <c- n>str</c-><c- p>;</c->
<c- n>copy</c-><c- p>.</c-><c- n>insert</c-><c- p>(</c-><c- mi>0</c-><c- p>,</c-> <c- n>view</c-><c- p>);</c->    <c- c1>// OK, but tedious and inefficient</c->
</pre>
   <p>Similarly, the current situation is asymmetric when considering
concatenation against raw pointers:</p>
<pre class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>str</c-><c- p>;</c->

<c- n>str</c-> <c- o>+</c-> <c- s>"hello"</c-><c- p>;</c->    <c- c1>// OK</c->
<c- n>str</c-> <c- o>+</c-> <c- s>"hello"</c-><c- n>sv</c-><c- p>;</c->  <c- c1>// ERROR</c->

<c- s>"hello"</c->   <c- o>+</c-> <c- n>str</c-><c- p>;</c->  <c- c1>// OK</c->
<c- s>"hello"</c-><c- n>sv</c-> <c- o>+</c-> <c- n>str</c-><c- p>;</c->  <c- c1>// ERROR</c->
</pre>
   <p>All of this is just bad ergonomics; the lack of <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> is
extremely surprising for end-users
(cf. <a data-link-type="biblio" href="#biblio-stackoverflow" title="Why is there no support for concatenating std::string and std::string_view?">this StackOverflow question</a>),
and harms teachability and usability of <code class="highlight"><c- n>string_view</c-></code> in lieu of raw
pointers.</p>
   <p>Now, as shown above, there <em>are</em> workarounds available either in terms
of named functions (<code class="highlight"><c- n>append</c-></code>, <code class="highlight"><c- n>insert</c-></code>, ...) or explicit conversions.
However it’s hard to steer users away from the convenience syntax
(which is ultimately the point of using <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> in the first
place). The availability of the <em>other</em> overloads of <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> opens
the door to bad code; for instance, it risks neglecting the value of
view classes:</p>
<pre class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- nf>prepend</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>string_view</c-> <c- n>prefix</c-><c- p>)</c->
<c- p>{</c->
  <c- k>return</c-> <c- n>std</c-><c- o>::</c-><c- n>string</c-><c- p>(</c-><c- n>prefix</c-><c- p>)</c-> <c- o>+</c-> <c- n>get_string</c-><c- p>();</c-> <c- c1>// inefficient</c->
<c- p>}</c->
</pre>
   <p>And it may even open the door to (subtle) bugs:</p>
<pre class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>result1</c-> <c- o>=</c-> <c- n>str</c-> <c- o>+</c-> <c- n>view</c-><c- p>;</c-> <c- c1>// ERROR. &lt;Sigh>, ok, let me rewrite as...</c->

<c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>result2</c-> <c- o>=</c-> <c- n>str</c-> <c- o>+</c-> <c- n>std</c-><c- o>::</c-><c- n>string</c-><c- p>(</c-><c- n>view</c-><c- p>);</c-> <c- c1>// OK, but this is inefficient. How about...</c->

<c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>result3</c-> <c- o>=</c-> <c- n>str</c-> <c- o>+</c-> <c- n>view</c-><c- p>.</c-><c- n>data</c-><c- p>();</c-> <c- c1>// Compiles; but BUG!</c->
</pre>
   <p>The last line exhibits undefined behavior if <code class="highlight"><c- n>view</c-></code> is not NUL terminated,
and also behaves differently in case it has embedded NULs.</p>
   <p>This paper proposes to fix these API flaws by adding suitable <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> overloads between string and string view classes. The
changes required for such operators are straightforward and should pose
no burden on implementations.</p>
   <h3 class="heading settled" data-level="2.1" id="why-are-those-overloads-missing-in-the-first-place"><span class="secno">2.1. </span><span class="content">Why are those overloads missing in the first place?</span><a class="self-link" href="#why-are-those-overloads-missing-in-the-first-place"></a></h3>
   <p><a data-link-type="biblio" href="#biblio-n3685" title="string_view: a non-owning reference to a string, revision 4">[N3685]</a> ("<code class="highlight"><c- n>string_view</c-></code>: a non-owning reference to a string, revision
4") offers the reason:</p>
   <blockquote>
    <p>I also omitted <code class="highlight"><c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>basic_string</c-><c- p>,</c-> <c- n>basic_string_view</c-><c- p>)</c-></code> because LLVM
returns a lightweight object from this overload and only performs the
concatenation lazily. If we define this overload, we’ll have a hard
time introducing that lightweight concatenation later.</p>
   </blockquote>
   <p>Subsequent revisions of the paper no longer have this paragraph.</p>
   <p>There is a couple of considerations that we think are important here.</p>
   <ul>
    <li data-md>
     <p><code class="highlight"><c- n>string_view</c-></code> has been approved for C++17 in Jacksonville (February
2016). At the time of this writing, such a "string builder" facility
has not been proposed for standardization (as far as we know).
Neglecting a completely reasonable feature to users (concatenation via <code class="highlight"><c- k>operator</c-><c- o>+</c-></code>) for so long, in the name of an yet unseen future "major"
feature, is a disservice to them.</p>
    <li data-md>
     <p>We strongly feel that overloading <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> is completely outside
of the design space for a string builder class. There is absolutely no
reason why <code class="highlight"><c- n>str</c-> <c- o>+</c-> <c- s>"hello"</c-><c- n>sv</c-></code> should use the builder, but <code class="highlight"><c- n>str</c-> <c- o>+</c-> <c- s>"hello"</c-></code> should not -- not to mention cases like <code class="highlight"><c- n>strA</c-> <c- o>+</c-> <c- n>strB</c-> <c- o>+</c-> <c- n>strC</c-></code>.
One cannot however change the semantics of the existing <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> overloads without breaking API/ABI compatibility.
In Qt, <a data-link-type="biblio" href="#biblio-qstringbuilder" title="QStringBuilder documentation">[QStringBuilder]</a> uses <code class="highlight"><c- k>operator</c-><c- o>%</c-></code> by default; blindly replacing <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> with <code class="highlight"><c- k>operator</c-><c- o>%</c-></code> when concatenating strings
comes with its own share of problems (not only it is API incompatible,
but it causes <a data-link-type="biblio" href="#biblio-clazy-qstringbuilder" title="auto-unexpected-qstringbuilder">dangling references</a> in a number of scenarios).</p>
   </ul>
   <p>In short: we do not see any reason to further withhold the proposed
additions.</p>
   <h2 class="heading settled" data-level="3" id="impact"><span class="secno">3. </span><span class="content">Impact On The Standard</span><a class="self-link" href="#impact"></a></h2>
   <p>This proposal is a pure library extension.</p>
   <p>This proposal does not depend on any other library extensions.</p>
   <p>This proposal does not require any changes in the core language.</p>
   <h2 class="heading settled" data-level="4" id="designdecisions"><span class="secno">4. </span><span class="content">Design Decisions</span><a class="self-link" href="#designdecisions"></a></h2>
   <h3 class="heading settled" data-level="4.1" id="minimizingallocations"><span class="secno">4.1. </span><span class="content">Minimizing the number of allocations</span><a class="self-link" href="#minimizingallocations"></a></h3>
   <p>The proposed wording builds on top / reuses of the existing one for <code class="highlight"><c- n>CharT</c-> <c- o>*</c-></code>. In particular, no attempts have been made at e.g. minimizing
memory allocations (by allocating only one buffer of suitable size,
then concatenating in that buffer). Implementations already employ such
mechanisms internally, and we would expect them to do the same also for
the new overloads (for instance, see <a data-link-type="biblio" href="#biblio-libstdcpp-string-concatenation" title="operator+ between pointer and string in libstdc++">here</a> for libstdc++ and <a data-link-type="biblio" href="#biblio-libcpp-string-concatenation" title="operator+ between pointer and string in libc++">here</a> for libc++).</p>
   <h3 class="heading settled" data-level="4.2" id="hiddenfriends"><span class="secno">4.2. </span><span class="content">Should the proposed operators be hidden friends? Should they be function templates?</span><a class="self-link" href="#hiddenfriends"></a></h3>
   <p>There are several ways to define the proposed overloads.</p>
   <h4 class="heading settled" data-level="4.2.1" id="approach-1-free-non-friend-function-templates-taking-exactly-a-string-view"><span class="secno">4.2.1. </span><span class="content">Approach 1: free non-friend function templates, taking exactly a string view</span><a class="self-link" href="#approach-1-free-non-friend-function-templates-taking-exactly-a-string-view"></a></h4>
   <p>The signature would look like this:</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-> <c- o>=</c-> <c- n>char_traits</c-><c- o>&lt;</c-><c- n>charT</c-><c- o>></c-><c- p>,</c->
         <c- k>class</c-> <c- nc>Allocator</c-> <c- o>=</c-> <c- n>allocator</c-><c- o>&lt;</c-><c- n>charT</c-><c- o>>></c->
  <c- k>class</c-> <c- nc>basic_string</c-> <c- p>{</c->
    <c- c1>// [...]</c->
  <c- p>};</c->

<c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-><c- p>,</c-> <c- k>class</c-> <c- nc>Allocator</c-><c- o>></c->
<c- k>constexpr</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c->
  <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>>&amp;</c-> <c- n>lhs</c-><c- p>,</c->
            <c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- o>></c-> <c- n>rhs</c-><c- p>);</c->
<c- c1>// Repeat for the other overloads with swapped arguments, rvalues, etc.</c->
</pre>
   <p>This approach closely follows the pre-existing overloads for <code class="highlight"><c- k>operator</c-><c- o>+</c-></code>. In particular, here the newly added operators are not
hidden friends (which may increase compilation times, give worse
compile errors, etc.).</p>
   <p>Still: just like hidden friends, it is <strong>not</strong> possible to use these
operator with datatypes implicitly convertible to <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>basic_string</c-></code> / <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>basic_string_view</c-></code> specializations:</p>
<pre class="highlight"><c- k>class</c-> <c- nc>convertible_to_string</c->
<c- p>{</c->
<c- k>public</c-><c- o>:</c->
  <c- d>/* implicit */</c-> <c- k>operator</c-> <c- n>std</c-><c- o>::</c-><c- n>string</c-><c- p>()</c-> <c- k>const</c-><c- p>;</c->
<c- p>};</c->

<c- n>convertible_to_string</c-> <c- n>cts</c-><c- p>;</c->

<c- n>cts</c-> <c- o>+</c-> <c- s>"hello"</c-><c- n>s</c-><c- p>;</c->    <c- c1>// ERROR (pre-existing)</c->
<c- n>cts</c-> <c- o>+</c-> <c- s>"hello"</c-><c- n>sv</c-><c- p>;</c->   <c- c1>// ERROR</c->
</pre>
   <p>The error stems from the fact that the existing (and the proposed) <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> are function templates, and implicit conversions are not
possible given the signatures of these functions: all the parameter
types of <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> contain a <em>template-parameter</em> that needs to be
deduced, in which case implicit conversions are not considered
(this is [temp.arg.explicit/7]).</p>
   <p>While the lack of support for types implictly convertible to strings
may be desiderable (for symmetry), the lack of support for types
implictly convertible to string <em>views</em> is questionable. String view
operations explictly support objects of types convertible to them. For
instance:</p>
<pre class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>s</c-><c- p>;</c->
<c- n>convertible_to_string</c-> <c- n>cts</c-><c- p>;</c->

<c- n>s</c-> <c- o>==</c-> <c- n>cts</c-><c- p>;</c->   <c- c1>// ERROR</c->


<c- n>std</c-><c- o>::</c-><c- n>string_view</c-> <c- n>sv</c-><c- p>;</c->
<c- n>convertible_to_string_view</c-> <c- n>ctsv</c-><c- p>;</c->

<c- n>sv</c-> <c- o>==</c-> <c- n>ctsv</c-><c- p>;</c-> <c- c1>// OK; [string.view.comparison/1]</c->
</pre>
   <p>The above definition of the overloads would prevent types convertible
to string views to be appended/prepended to strings, again because the
implicit conversion towards the string view type would be prevented.
This would even be inconsistent with the existing string’s member
functions:</p>
<pre class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>s</c-><c- p>;</c->
<c- n>convertible_to_string_view</c-> <c- n>ctsv</c-><c- p>;</c->

<c- n>s</c-><c- p>.</c-><c- n>append</c-><c- p>(</c-><c- n>ctsv</c-><c- p>);</c-> <c- c1>// OK, [string.append/3]</c->
<c- n>s</c-> <c- o>+</c-> <c- n>ctsv</c-><c- p>;</c->       <c- c1>// ERROR, ???</c->
</pre>
   <p>Finally, overloads added as non-member/non-friend function templates
are not viable when using something like <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>reference_wrapper</c-></code>:</p>
<pre class="highlight"><c- n>std</c-><c- o>::</c-><c- n>reference_wrapper</c-><c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- n>string</c-><c- o>></c-> <c- n>rs</c-><c- p>(</c-><c- o>~~~</c-><c- p>);</c->
<c- n>std</c-><c- o>::</c-><c- n>reference_wrapper</c-><c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- n>string_view</c-><c- o>></c-> <c- n>rsv</c-><c- p>(</c-><c- o>~~~</c-><c- p>);</c->

<c- n>rs</c-> <c- o>+</c-> <c- n>rs</c-><c- p>;</c->  <c- c1>// ERROR (pre-existing)</c->
<c- n>rs</c-> <c- o>+</c-> <c- n>rsv</c-><c- p>;</c-> <c- c1>// ERROR</c->
</pre>
   <p>This is because an argument of type e.g. <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>reference_wrapper</c-><c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- n>string</c-><c- o>></c-></code> (i.e. <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>reference_wrapper</c-><c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- n>basic_string</c-><c- o>&lt;</c-><c- b>char</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>char_traits</c-><c- o>&lt;</c-><c- b>char</c-><c- o>></c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>allocator</c-><c- o>&lt;</c-><c- b>char</c-><c- o>>>></c-></code>)
can never match against a parameter of type <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c-></code>.</p>
   <h4 class="heading settled" data-level="4.2.2" id="approach-2-free-non-friend-function-templates-taking-anything-convertible-to-a-string-view"><span class="secno">4.2.2. </span><span class="content">Approach 2: free non-friend function templates, taking anything convertible to a string view</span><a class="self-link" href="#approach-2-free-non-friend-function-templates-taking-anything-convertible-to-a-string-view"></a></h4>
   <p>This is similar to approach n. 1, except that the string view argument
would also accept any type which is convertible to a string view. The
precedent for this would be the existing functions for
concatenating/inserting strings (e.g. <code class="highlight"><c- n>append</c-></code>, <code class="highlight"><c- n>insert</c-></code>, <code class="highlight"><c- k>operator</c-><c- o>+=</c-></code>), all of which take a parameter of any type <em>convertible</em> to a string view; as well as the comparison operators for string views,
where "[...] implementations shall provide sufficient additional overloads
[...] so that an object <code class="highlight"><c- n>t</c-></code> with an implicit conversion to <code class="highlight"><c- n>S</c-></code> can be
compared" ([string.view.comparison/1]).</p>
   <p>Therefore, the proposed signatures would look like this:</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-> <c- o>=</c-> <c- n>char_traits</c-><c- o>&lt;</c-><c- n>charT</c-><c- o>></c-><c- p>,</c->
         <c- k>class</c-> <c- nc>Allocator</c-> <c- o>=</c-> <c- n>allocator</c-><c- o>&lt;</c-><c- n>charT</c-><c- o>>></c->
  <c- k>class</c-> <c- nc>basic_string</c-> <c- p>{</c->
    <c- c1>// [...]</c->
  <c- p>};</c->

<c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-><c- p>,</c-> <c- k>class</c-> <c- nc>Allocator</c-><c- o>></c->
<c- k>constexpr</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c->
  <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>>&amp;</c-> <c- n>lhs</c-><c- p>,</c->
            <c- n>type_identity_t</c-><c- o>&lt;</c-><c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- o>>></c-> <c- n>rhs</c-><c- p>);</c->
  <c- c1>//        ^^^^^^^^^^^^^^^ make a non-deduced context</c->


<c- c1>// Repeat for the other overloads with swapped arguments, rvalues, etc.</c->
</pre>
   <p class="note" role="note"><span class="marker">Note:</span> this may or may not be the actual <em>proposed wording</em>. We
could instead handwave the actual overload set by using the "sufficient
additional overloads" wording. An implementation could therefore choose
to use another implementation strategy, such as SFINAE, constraints,
and so on. (See also <a data-link-type="biblio" href="#biblio-lwg3950" title="std::basic_string_view comparison operators are overspecified">[LWG3950]</a>.)</p>
   <p>Apart from allowing to concatenate strings with objects of types <em>convertible</em> to string views, this approach still forbids the usage of types convertible to
strings, as well as types such as <code class="highlight"><c- n>reference_wrapper</c-></code>:</p>
<pre class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>s</c-><c- p>;</c->
<c- n>std</c-><c- o>::</c-><c- n>string_view</c-> <c- n>sv</c-><c- p>;</c->
<c- n>convertible_to_string</c-> <c- n>cts</c-><c- p>;</c->
<c- n>convertible_to_string_view</c-> <c- n>ctsv</c-><c- p>;</c->

<c- n>s</c-> <c- o>+</c-> <c- n>sv</c-><c- p>;</c->    <c- c1>// OK</c->
<c- n>s</c-> <c- o>+</c-> <c- n>ctsv</c-><c- p>;</c->  <c- c1>// OK</c->

<c- n>s</c-> <c- o>+</c-> <c- n>cts</c-><c- p>;</c->   <c- c1>// ERROR</c->
<c- n>cts</c-> <c- o>+</c-> <c- n>sv</c-><c- p>;</c->  <c- c1>// ERROR</c->
</pre>
   <h4 class="heading settled" data-level="4.2.3" id="approach-3-hidden-friends-non-template-functions"><span class="secno">4.2.3. </span><span class="content">Approach 3: hidden friends, non-template functions</span><a class="self-link" href="#approach-3-hidden-friends-non-template-functions"></a></h4>
   <p>Basically, this would be an application of the Barton–Nackman idiom in
combination with hidden friends ([hidden.friends]).</p>
   <p>The proposed operators would look like this:</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-> <c- o>=</c-> <c- n>char_traits</c-><c- o>&lt;</c-><c- n>charT</c-><c- o>></c-><c- p>,</c->
         <c- k>class</c-> <c- nc>Allocator</c-> <c- o>=</c-> <c- n>allocator</c-><c- o>&lt;</c-><c- n>charT</c-><c- o>>></c->
  <c- k>class</c-> <c- nc>basic_string</c-> <c- p>{</c->
    <c- c1>// [...]</c->

    <c- k>constexpr</c-> <c- k>friend</c-> <c- n>basic_string</c->
      <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_string</c-><c- o>&amp;</c-> <c- n>lhs</c-><c- p>,</c->
                <c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- o>></c-><c- p>)</c-> <c- p>{</c-> <c- d>/* hidden friend */</c-> <c- p>}</c->
    <c- c1>// Repeat for the other overloads with swapped arguments, rvalues, etc.</c->
  <c- p>};</c->
</pre>
   <p>In such an approach, one of the arguments must still be a string
object, otherwise the overload is not even added to the overload set
(hidden friend).</p>
   <p>The other argument can be any object implicitly convertible to a string
view. Since the overload is not a function template, implicit
conversions here "kick in" and work as expected, without the need of
adding additional overloads (or declaring the operators as function
templates):</p>
<pre class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>s</c-><c- p>;</c->
<c- n>convertible_to_string_view</c-> <c- n>ctsv</c-><c- p>;</c->

<c- n>s</c-> <c- o>+</c-> <c- n>ctsv</c-><c- p>;</c->  <c- c1>// OK</c->
</pre>
   <p><strong>There is a perhaps surprising side-effect</strong>, however: defining this
overload set would also allow concatenation between a string and an
object convertible to a <em>string</em>. For instance:</p>
<pre class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>s</c-><c- p>;</c->
<c- n>convertible_to_string</c-> <c- n>cts</c-><c- p>;</c->

<c- n>s</c-> <c- o>==</c-> <c- n>cts</c-><c- p>;</c->  <c- c1>// ERROR</c->
<c- n>s</c-> <c- o>+</c->  <c- n>cts</c-><c- p>;</c->  <c- c1>// OK (!)</c->
</pre>
   <p>In the last line, the lhs of type <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-></code> makes the various <code class="highlight"><c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>string</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>string_view</c-><c- p>)</c-></code> overloads visible to lookup.
Then, the <code class="highlight"><c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>string_view</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>string</c-><c- o>&amp;&amp;</c-><c- p>)</c-></code> is selected,
converting the <code class="highlight"><c- n>lhs</c-></code> from <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-></code> to <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string_view</c-></code> and the <code class="highlight"><c- n>rhs</c-></code> from <code class="highlight"><c- n>convertible_to_string</c-></code> to a rvalue <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-></code>.</p>
   <p>Finally, using types such as <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>reference_wrapper</c-></code> would work
transparently:</p>
<pre class="highlight"><c- n>std</c-><c- o>::</c-><c- n>reference_wrapper</c-><c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- n>string</c-><c- o>></c-> <c- n>rs</c-><c- p>(</c-><c- o>~~~</c-><c- p>);</c->

<c- n>rs</c-> <c- o>+</c-> <c- s>"hello"</c-><c- n>sv</c-><c- p>;</c-> <c- c1>// OK</c->
</pre>
   <p>In this example, ADL would add the hidden friend operators to the
overload set (cf. [basic.lookup.argdep/3.2]), operators which again are
non-template functions. Then, the <code class="highlight"><c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- k>const</c-> <c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- o>&amp;</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>string_view</c-><c- p>)</c-></code> is selected, since we can implicitly convert the
first parameter from the argument of type <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>reference_wrapper</c-><c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- n>string</c-><c- o>></c-></code>.</p>
   <h4 class="heading settled" data-level="4.2.4" id="approach-4-hidden-friends-function-templates-taking-anything-convertible-to-a-string-view"><span class="secno">4.2.4. </span><span class="content">Approach 4: hidden friends, function templates, taking anything convertible to a string view</span><a class="self-link" href="#approach-4-hidden-friends-function-templates-taking-anything-convertible-to-a-string-view"></a></h4>
   <p>This approach is similar to approach 2, however makes the proposed
operators hidden friends.</p>
   <p>The proposed operators would in principle look like this:</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-> <c- o>=</c-> <c- n>char_traits</c-><c- o>&lt;</c-><c- n>charT</c-><c- o>></c-><c- p>,</c->
         <c- k>class</c-> <c- nc>Allocator</c-> <c- o>=</c-> <c- n>allocator</c-><c- o>&lt;</c-><c- n>charT</c-><c- o>>></c->
  <c- k>class</c-> <c- nc>basic_string</c-> <c- p>{</c->
    <c- c1>// [...]</c->

    <c- k>template</c-> <c- o>&lt;</c-><c- k>class</c-> <c- nc>C</c-><c- p>,</c-> <c- k>class</c-> <c- nc>T</c-><c- p>,</c-> <c- k>class</c-> <c- nc>A</c-><c- o>></c->
      <c- k>constexpr</c-> <c- k>friend</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>C</c-><c- p>,</c-> <c- n>T</c-><c- p>,</c-> <c- n>A</c-><c- o>></c->
        <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>C</c-><c- p>,</c-> <c- n>T</c-><c- p>,</c-> <c- n>A</c-><c- o>>&amp;</c-> <c- n>lhs</c-><c- p>,</c->
                  <c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>C</c-><c- p>,</c-> <c- n>T</c-><c- o>></c-><c- p>)</c-> <c- p>{</c-> <c- d>/* hidden friend */</c-> <c- p>}</c->

    <c- k>template</c-> <c- o>&lt;</c-><c- k>class</c-> <c- nc>C</c-><c- p>,</c-> <c- k>class</c-> <c- nc>T</c-><c- p>,</c-> <c- k>class</c-> <c- nc>A</c-><c- o>></c->
      <c- k>constexpr</c-> <c- k>friend</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>C</c-><c- p>,</c-> <c- n>T</c-><c- p>,</c-> <c- n>A</c-><c- o>></c->
        <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>C</c-><c- p>,</c-> <c- n>T</c-><c- p>,</c-> <c- n>A</c-><c- o>>&amp;</c-> <c- n>lhs</c-><c- p>,</c->
                  <c- n>type_identity_t</c-><c- o>&lt;</c-><c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>C</c-><c- p>,</c-> <c- n>T</c-><c- o>>></c-><c- p>)</c-> <c- p>{</c-> <c- d>/* hidden friend */</c-> <c- p>}</c->

    <c- c1>// Repeat for the other overloads with swapped arguments, rvalues, etc.</c->
  <c- p>};</c->
</pre>
   <p>In practice the above does <em>not work</em>, as it leads to redefinition
errors if <code class="highlight"><c- n>basic_string</c-></code> is instatiated with different template
parameters (which is of course the case). Note that, in order for the
operators to be hidden friends, their <em>definition</em> must be present in <code class="highlight"><c- n>basic_string</c-></code>'s class body; multiple instantiations of <code class="highlight"><c- n>basic_string</c-></code> would therefore redefine the same function template multiple times.</p>
   <p>Hence, an actual implementation has to employ some tricks, such as
isolating the operators in a non-template base class:</p>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-><c- p>,</c-> <c- k>class</c-> <c- nc>Allocator</c-><c- o>></c->
  <c- k>class</c-> <c- nc>basic_string</c-><c- p>;</c->

<c- k>class</c-> <c- nc>__basic_string_base</c-> <c- c1>// exposition-only</c->
<c- p>{</c->
  <c- k>template</c-> <c- o>&lt;</c-><c- k>class</c-> <c- nc>C</c-><c- p>,</c-> <c- k>class</c-> <c- nc>T</c-><c- p>,</c-> <c- k>class</c-> <c- nc>A</c-><c- o>></c->
    <c- k>constexpr</c-> <c- k>friend</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>C</c-><c- p>,</c-> <c- n>T</c-><c- p>,</c-> <c- n>A</c-><c- o>></c->
      <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>C</c-><c- p>,</c-> <c- n>T</c-><c- p>,</c-> <c- n>A</c-><c- o>>&amp;</c-> <c- n>lhs</c-><c- p>,</c->
                <c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>C</c-><c- p>,</c-> <c- n>T</c-><c- o>></c-><c- p>)</c-> <c- p>{</c-> <c- d>/* hidden friend */</c-> <c- p>}</c->

  <c- k>template</c-> <c- o>&lt;</c-><c- k>class</c-> <c- nc>C</c-><c- p>,</c-> <c- k>class</c-> <c- nc>T</c-><c- p>,</c-> <c- k>class</c-> <c- nc>A</c-><c- o>></c->
    <c- k>constexpr</c-> <c- k>friend</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>C</c-><c- p>,</c-> <c- n>T</c-><c- p>,</c-> <c- n>A</c-><c- o>></c->
      <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>C</c-><c- p>,</c-> <c- n>T</c-><c- p>,</c-> <c- n>A</c-><c- o>>&amp;</c-> <c- n>lhs</c-><c- p>,</c->
                <c- n>type_identity_t</c-><c- o>&lt;</c-><c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>C</c-><c- p>,</c-> <c- n>T</c-><c- o>>></c-><c- p>)</c-> <c- p>{</c-> <c- d>/* hidden friend */</c-> <c- p>}</c->

  <c- c1>// Repeat for the other overloads with swapped arguments, rvalues, etc.</c->
<c- p>};</c->

<c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-> <c- o>=</c-> <c- n>char_traits</c-><c- o>&lt;</c-><c- n>charT</c-><c- o>></c-><c- p>,</c->
         <c- k>class</c-> <c- nc>Allocator</c-> <c- o>=</c-> <c- n>allocator</c-><c- o>&lt;</c-><c- n>charT</c-><c- o>>></c->
  <c- k>class</c-> <c- nc>basic_string</c-> <c- o>:</c-> <c- n>__basic_string_base</c-> <c- p>{</c->
    <c- c1>// [...]</c->
  <c- p>};</c->
</pre>
   <p>This approach brings the same semantics as of approach 2, with
the exception that the operators are not found through ordinary
unqualified/qualified lookup (because they are hidden friends).
It is still not possible to call these operators using an
argument of a type convertible to string, nor to call them through <code class="highlight"><c- n>reference_wrapper</c-></code>.</p>
   <h4 class="heading settled" data-level="4.2.5" id="summary"><span class="secno">4.2.5. </span><span class="content">Summary</span><a class="self-link" href="#summary"></a></h4>
   <table>
    <thead>
     <tr>
      <td>Works between...?
      <td>Approach 1
      <td>Approach 2
      <td>Approach 3
      <td>Approach 4
    <tbody>
     <tr>
      <td><code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-></code> and <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string_view</c-></code>
      <td>✔
      <td>✔
      <td>✔
      <td>✔
      <td>
     <tr>
      <td><code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-></code> and an object convertible to <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string_view</c-></code>
      <td>✘
      <td>✔
      <td>✔
      <td>✔
     <tr>
      <td><code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-></code> and an object convertible to <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-></code>
      <td>✘
      <td>✘
      <td>✔
      <td>✔
     <tr>
      <td>Two objects convertible to <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string</c-></code>
      <td>✘
      <td>✘
      <td>✘
      <td>✘
     <tr>
      <td><code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>reference_wrapper</c-><c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- n>string</c-><c- o>></c-></code> and <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string_view</c-></code>
      <td>✘
      <td>✘
      <td>✔
      <td>✘
   </table>
   <h4 class="heading settled" data-level="4.2.6" id="which-strategy-should-be-used"><span class="secno">4.2.6. </span><span class="content">Which strategy should be used?</span><a class="self-link" href="#which-strategy-should-be-used"></a></h4>
   <p>The R1 revision of this paper implemented approach 2, for symmetry with
the pre-existing overloads of <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> between strings.</p>
   <p>During the 2022-08-16 LEWG telecon, a poll indicated weak consensus
(2/5/4/2/0) for making the proposed operators hidden friends, even at
the cost of making them inconsistent with the existing overloads.</p>
   <p>R3 of this paper implemented approach 3, and elaborated on the
consequences of the different approaches, including the (possibly
unexpected) ability of concatenating objects of types convertible to <em>strings</em>. During the review of R3 in the LEWG telecon on 2023-04-18,
when presented with this information, there was no longer consensus for
the hidden friends approach.</p>
   <p>Therefore, in R4 we are reverting to approach 2, again with the idea of
keeping the overload set consistent with the pre-existing overloads.</p>
   <h3 class="heading settled" data-level="4.3" id="backwards-compatibility-and-annex-c"><span class="secno">4.3. </span><span class="content">Backwards compatibility and Annex C</span><a class="self-link" href="#backwards-compatibility-and-annex-c"></a></h3>
   <p>Library Evolution has requested a note to be added to Annex C in
case the proposed operators break backwards compatibility.</p>
   <p>If users define an <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> overload between classes from the
Standard Library (in another namespace than <code class="highlight"><c- n>std</c-></code>), and then the
Standard Library starts providing such an overload and user code stops
compiling (due to redefinitions, ambiguities, etc.), does this
constitute a source-incompatible change?</p>
   <p><a data-link-type="biblio" href="#biblio-sd-8" title="SD-8: Standard Library Compatibility">[SD-8]</a> is not particularly explicit on the subject of adding
new overloads for <em>operators</em>, although it does state that:</p>
   <blockquote>
    <p>Primarily, the standard reserves the right to:</p>
    <p>[...]</p>
    <ul>
     <li data-md>
      <p>Add new names to any entity within any reserved namespace, including but not limited to:</p>
      <ul>
       <li data-md>
        <p>Functions (this includes new member functions and overloads to existing functions)</p>
      </ul>
    </ul>
   </blockquote>
   <p>Operators are functions, but they’re also a particular class of them,
as they are practically never called using an explicit function-call
expression. Instead, any ordinary code relies on the special rules of
overload resolution for operators ([over.match.oper]).</p>
   <p>The question here is therefore is whether the Standard Library is
simply allowed to alter the overload set available to operators, when
they are used on objects of datatypes defined in the library itself. It
is easy to argue that, if <em>both</em> arguments to an operator overload
are library datatypes, then the library reserves the right to add
such overload without worrying about any possible breakage. Implicit
conversions and ADL make however the situation slightly more complex.</p>
   <p>We can construct an example as follows. The proposed <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> overloads require one of the arguments to be an object of a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>basic_string</c-></code> specialization (see the discussion above
regarding [temp.arg.explicit/7]). Let’s therefore focus on the <em>other</em> argument’s type.</p>
   <p>Suppose that a user declared a <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> overload like this:</p>
<pre class="highlight"><c- k>struct</c-> <c- nc>user_datatype</c-><c- p>;</c->

<c- n>R</c-> <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>string</c-><c- p>,</c-> <c- n>user_datatype</c-><c- p>);</c->
</pre>
   <p>Then this overload will always be preferred to the ones that we are
proposing (when passing a parameter of type <code class="highlight"><c- n>user_datatype</c-></code>). This works
even if the type is implictly convertible to <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string_view</c-></code> and therefore
overload resolution does not exclude the overloads of the present proposal:</p>
<pre class="highlight"><c- k>struct</c-> <c- nc>convertible_to_string_view</c->
<c- p>{</c->
  <c- d>/* implicit */</c-> <c- k>operator</c-> <c- n>std</c-><c- o>::</c-><c- n>string_view</c-><c- p>()</c-> <c- k>const</c-><c- p>;</c->
<c- p>};</c->
<c- n>R</c-> <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>string</c-><c- p>,</c-> <c- n>convertible_to_string_view</c-><c- p>);</c-> <c- c1>// pre-existing</c->

<c- n>convertible_to_string_view</c-> <c- n>ctsv</c-><c- p>;</c->

<c- s>"hello"</c-><c- n>s</c-> <c- o>+</c-> <c- n>ctsv</c-><c- p>;</c-> <c- c1>// still calls the user-defined operator+, as it’s a better match</c->
</pre>
   <p>Let’s furthermore consider a further type convertible to both a
user-defined datatype as well as <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string_view</c-></code>. This could be, for
instance, a type convertible to a pre-C++17 custom string view class
which has also been "modernized" by adding a conversion to <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string_view</c-></code>:</p>
<pre class="highlight"><c- k>struct</c-> <c- nc>my_string_view</c-><c- p>;</c-> <c- c1>// pre-c++17, legacy</c->
<c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>string</c-><c- p>,</c-> <c- n>my_string_view</c-><c- p>);</c->

<c- k>struct</c-> <c- nc>char_buffer</c->
<c- p>{</c->
  <c- d>/* implicit */</c-> <c- k>operator</c-> <c- n>my_string_view</c-><c- p>()</c-> <c- k>const</c-><c- p>;</c->   <c- c1>// legacy</c->
  <c- d>/* implicit */</c-> <c- k>operator</c-> <c- n>std</c-><c- o>::</c-><c- n>string_view</c-><c- p>()</c-> <c- k>const</c-><c- p>;</c-> <c- c1>// modern</c->
<c- p>};</c->


<c- n>char_buffer</c-> <c- n>buf</c-><c- p>;</c->
<c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>result</c-> <c- o>=</c-> <c- s>"hello"</c-><c- n>s</c-> <c- o>+</c-> <c- n>buf</c-><c- p>;</c-> <c- c1>// OK</c->
</pre>
   <p>Although it may seem that the call to <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> would now be
ambiguous between <code class="highlight"><c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>string</c-><c- p>,</c-> <c- n>my_string_view</c-><c- p>)</c-></code> and <code class="highlight"><c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>string</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>string_view</c-><c- p>)</c-></code>, it actually is <em>not
ambiguous</em> and even calls the <em>pre-existing</em> <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> taking a <code class="highlight"><c- n>my_string_view</c-></code>. The reason for this is that although both <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> overloads are viable, the one taking a <code class="highlight"><c- n>my_string_view</c-></code> as defined
above is not a function template, while the one taking a <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>string_view</c-></code> is actually a function template specialization; the
former overload ranks better ([over.match.best.general]/2.4).</p>
   <p>What if the user-defined <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> is itself a function template?
For instance:</p>
<pre class="highlight"><c- k>template</c-> <c- o>&lt;</c-><c- k>typename</c-> <c- nc>Char</c-><c- o>></c->
<c- k>struct</c-> <c- nc>basic_my_string_view</c-><c- p>;</c->

<c- k>using</c-> <c- n>my_string_view</c-> <c- o>=</c-> <c- n>basic_my_string_view</c-><c- o>&lt;</c-><c- b>char</c-><c- o>></c-><c- p>;</c->

<c- k>template</c-> <c- o>&lt;</c-><c- k>typename</c-> <c- nc>Char</c-><c- o>></c->
  <c- n>std</c-><c- o>::</c-><c- n>basic_string</c-><c- o>&lt;</c-><c- n>Char</c-><c- o>></c->
    <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>basic_string</c-><c- o>&lt;</c-><c- n>Char</c-><c- o>></c-><c- p>,</c-> <c- n>basic_my_string_view</c-><c- o>&lt;</c-><c- n>Char</c-><c- o>></c-><c- p>);</c->

<c- k>struct</c-> <c- nc>char_buffer</c->
<c- p>{</c->
  <c- d>/* implicit */</c-> <c- k>operator</c-> <c- n>my_string_view</c-><c- p>()</c-> <c- k>const</c-><c- p>;</c->
  <c- d>/* implicit */</c-> <c- k>operator</c-> <c- n>std</c-><c- o>::</c-><c- n>string_view</c-><c- p>()</c-> <c- k>const</c-><c- p>;</c->
<c- p>};</c->


<c- n>char_buffer</c-> <c- n>buf</c-><c- p>;</c->
<c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>result</c-> <c- o>=</c-> <c- s>"hello"</c-><c- n>s</c-> <c- o>+</c-> <c- n>buf</c-><c- p>;</c-> <c- c1>// was: ERROR; with the proposed changes: OK</c->
</pre>
   <p>The above code does not compile without the changes introduced by this
paper, again because implicit conversions are not considered due to
the deducible <code class="highlight"><c- n>Char</c-></code> template parameter. With the changes introduced by
this paper, the code now compiles, and <code class="highlight"><c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>string</c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>string_view</c-><c- p>)</c-></code> is called; the pre-existing overload is still not
viable. In other words: in this specific scenario the impact is positive.</p>
   <p>What if user code employed some technique to enable implicit conversions
with <code class="highlight"><c- k>operator</c-><c- o>+</c-></code>, for instance like this:</p>
<pre class="highlight"><c- k>template</c-> <c- o>&lt;</c-><c- k>typename</c-> <c- nc>Char</c-><c- o>></c->
<c- k>struct</c-> <c- nc>basic_my_string_view</c-><c- p>;</c->

<c- k>using</c-> <c- n>my_string_view</c-> <c- o>=</c-> <c- n>basic_my_string_view</c-><c- o>&lt;</c-><c- b>char</c-><c- o>></c-><c- p>;</c->

<c- k>template</c-> <c- o>&lt;</c-><c- k>typename</c-> <c- nc>Char</c-><c- o>></c->
  <c- n>std</c-><c- o>::</c-><c- n>basic_string</c-><c- o>&lt;</c-><c- n>Char</c-><c- o>></c->
    <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>basic_string</c-><c- o>&lt;</c-><c- n>Char</c-><c- o>></c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>type_identity_t</c-><c- o>&lt;</c-><c- n>basic_my_string_view</c-><c- o>&lt;</c-><c- n>Char</c-><c- o>>></c-><c- p>);</c->

<c- k>struct</c-> <c- nc>char_buffer</c->
<c- p>{</c->
  <c- d>/* implicit */</c-> <c- k>operator</c-> <c- n>my_string_view</c-><c- p>()</c-> <c- k>const</c-><c- p>;</c->
  <c- d>/* implicit */</c-> <c- k>operator</c-> <c- n>std</c-><c- o>::</c-><c- n>string_view</c-><c- p>()</c-> <c- k>const</c-><c- p>;</c->
<c- p>};</c->


<c- n>char_buffer</c-> <c- n>buf</c-><c- p>;</c->
<c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>result</c-> <c- o>=</c-> <c- s>"hello"</c-><c- n>s</c-> <c- o>+</c-> <c- n>buf</c-><c- p>;</c-> <c- c1>// was: OK; with the proposed changes: AMBIGUOUS</c->
</pre>
   <p>In this last scenario the call to <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> becomes ambiguous with
the proposed changes.</p>
   <p><a data-link-type="biblio" href="#biblio-sd-8" title="SD-8: Standard Library Compatibility">[SD-8]</a> does not seem to offer guidance here: is it OK for the
Standard Library to break code that is "too generous" in its implicit
conversions? In case, we are going to stay on the safe side, and
document this possible breakage in Annex C.</p>
   <h2 class="heading settled" data-level="5" id="implementationexperience"><span class="secno">5. </span><span class="content">Implementation experience</span><a class="self-link" href="#implementationexperience"></a></h2>
   <p>A working prototype of the changes proposed by this paper, done on top
of GCC 13.1, is available in this <a data-link-type="biblio" href="#biblio-p2591-gcc" title="P2591 prototype implementation for libstdc++">GCC branch</a> on GitHub.
The entire libstdc++ testsuite passes with the changes applied. A smoke
test is included.</p>
   <p>Will Hawkins has very kindly contributed <a data-link-type="biblio" href="#biblio-p2591-llvm" title="P2591 prototype implementation for libc++">an implementation</a> in libc++.</p>
   <h2 class="heading settled" data-level="6" id="technicalspecifications"><span class="secno">6. </span><span class="content">Technical Specifications</span><a class="self-link" href="#technicalspecifications"></a></h2>
   <p>All the proposed changes are relative to <a data-link-type="biblio" href="#biblio-n4950" title="Working Draft, Standard for Programming Language C++">[N4950]</a>.</p>
   <h3 class="heading settled" data-level="6.1" id="feature-testing-macro"><span class="secno">6.1. </span><span class="content">Feature testing macro</span><a class="self-link" href="#feature-testing-macro"></a></h3>
   <p>In [version.syn], modify</p>
<pre class="highlight"><c- cp>#define __cpp_­lib_­string_­view </c-><del><c- cp>201803L</c-></del><ins><c- cp>YYYYMML</c-></ins><c- cp> </c-><c- c1>// also in &lt;string>, &lt;string_­view></c->
</pre>
   <p>with the value specified as usual (year and month of adoption of the
present proposal).</p>
   <h3 class="heading settled" data-level="6.2" id="proposedwording"><span class="secno">6.2. </span><span class="content">Proposed wording</span><a class="self-link" href="#proposedwording"></a></h3>
   <p>Modify [string.syn] as shown:</p>
   <blockquote>
<pre class="highlight"><c- k>namespace</c-> <c- nn>std</c-> <c- p>{</c->
    <c- p>[...]</c->

  <c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-><c- p>,</c-> <c- k>class</c-> <c- nc>Allocator</c-><c- o>></c->
    <c- k>constexpr</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c->
      <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>>&amp;&amp;</c-> <c- n>lhs</c-><c- p>,</c->
                <c- n>charT</c-> <c- n>rhs</c-><c- p>);</c->
<ins>  <c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-><c- p>,</c-> <c- k>class</c-> <c- nc>Allocator</c-><c- o>></c->
    <c- k>constexpr</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c->
      <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>>&amp;</c-> <c- n>lhs</c-><c- p>,</c->
                <c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- o>></c-> <c- n>rhs</c-><c- p>);</c->
  <c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-><c- p>,</c-> <c- k>class</c-> <c- nc>Allocator</c-><c- o>></c->
    <c- k>constexpr</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c->
      <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>>&amp;&amp;</c-> <c- n>lhs</c-><c- p>,</c->
                <c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- o>></c-> <c- n>rhs</c-><c- p>);</c->
  <c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-><c- p>,</c-> <c- k>class</c-> <c- nc>Allocator</c-><c- o>></c->
    <c- k>constexpr</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c->
      <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- o>></c-> <c- n>lhs</c-><c- p>,</c->
                <c- k>const</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>>&amp;</c-> <c- n>rhs</c-><c- p>);</c->
  <c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-><c- p>,</c-> <c- k>class</c-> <c- nc>Allocator</c-><c- o>></c->
    <c- k>constexpr</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c->
      <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- o>></c-> <c- n>lhs</c-><c- p>,</c->
                <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>>&amp;&amp;</c-> <c- n>rhs</c-><c- p>);</c->
  <c- c1>// see [string.op.plus.string_view], sufficient additional overloads of concatenation operators</c->
</ins>

  <c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-><c- p>,</c-> <c- k>class</c-> <c- nc>Allocator</c-><c- o>></c->
    <c- k>constexpr</c-> <c- b>bool</c->
      <c- k>operator</c-><c- o>==</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>>&amp;</c-> <c- n>lhs</c-><c- p>,</c->
                 <c- k>const</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>>&amp;</c-> <c- n>rhs</c-><c- p>)</c-> <c- k>noexcept</c-><c- p>;</c->
</pre>
   </blockquote>
   <hr>
   <p><strong>Append</strong> the following content at the end of [string.op.plus]:</p>
   <blockquote>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-><c- p>,</c-> <c- k>class</c-> <c- nc>Allocator</c-><c- o>></c->
  <c- k>constexpr</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c->
    <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- k>const</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>>&amp;</c-> <c- n>lhs</c-><c- p>,</c->
              <c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- o>></c-> <c- n>rhs</c-><c- p>);</c->
</pre>
    <p>� <i>Effects</i>: Equivalent to:</p>
<pre class="highlight"><c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c-> <c- n>r</c-> <c- o>=</c-> <c- n>lhs</c-><c- p>;</c->
<c- n>r</c-><c- p>.</c-><c- n>append</c-><c- p>(</c-><c- n>rhs</c-><c- p>);</c->
<c- k>return</c-> <c- n>r</c-><c- p>;</c->
</pre>
   </blockquote>
   <blockquote>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-><c- p>,</c-> <c- k>class</c-> <c- nc>Allocator</c-><c- o>></c->
  <c- k>constexpr</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c->
    <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>>&amp;&amp;</c-> <c- n>lhs</c-><c- p>,</c->
              <c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- o>></c-> <c- n>rhs</c-><c- p>);</c->
</pre>
    <p>� <i>Effects</i>: Equivalent to:</p>
<pre class="highlight"><c- n>lhs</c-><c- p>.</c-><c- n>append</c-><c- p>(</c-><c- n>rhs</c-><c- p>);</c->
<c- k>return</c-> <c- n>std</c-><c- o>::</c-><c- n>move</c-><c- p>(</c-><c- n>lhs</c-><c- p>);</c->
</pre>
   </blockquote>
   <blockquote>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-><c- p>,</c-> <c- k>class</c-> <c- nc>Allocator</c-><c- o>></c->
  <c- k>constexpr</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c->
    <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- o>></c-> <c- n>lhs</c-><c- p>,</c->
              <c- k>const</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>>&amp;</c-> <c- n>rhs</c-><c- p>);</c->
</pre>
    <p>� <i>Effects</i>: Equivalent to:</p>
<pre class="highlight"><c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c-> <c- n>r</c-> <c- o>=</c-> <c- n>rhs</c-><c- p>;</c->
<c- n>r</c-><c- p>.</c-><c- n>insert</c-><c- p>(</c-><c- mi>0</c-><c- p>,</c-> <c- n>lhs</c-><c- p>);</c->
<c- k>return</c-> <c- n>r</c-><c- p>;</c->
</pre>
   </blockquote>
   <blockquote>
<pre class="highlight"><c- k>template</c-><c- o>&lt;</c-><c- k>class</c-> <c- nc>charT</c-><c- p>,</c-> <c- k>class</c-> <c- nc>traits</c-><c- p>,</c-> <c- k>class</c-> <c- nc>Allocator</c-><c- o>></c->
  <c- k>constexpr</c-> <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c->
    <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- o>></c-> <c- n>lhs</c-><c- p>,</c->
              <c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>>&amp;&amp;</c-> <c- n>rhs</c-><c- p>);</c->
</pre>
    <p>� <i>Effects</i>: Equivalent to:</p>
<pre class="highlight"><c- n>rhs</c-><c- p>.</c-><c- n>insert</c-><c- p>(</c-><c- mi>0</c-><c- p>,</c-> <c- n>lhs</c-><c- p>);</c->
<c- k>return</c-> <c- n>std</c-><c- o>::</c-><c- n>move</c-><c- p>(</c-><c- n>rhs</c-><c- p>);</c->
</pre>
   </blockquote>
   <hr>
   <p>Add a new subclause after [string.op.plus] with the following content:</p>
   <blockquote>
     <strong>� Concatenation of strings and string views [string.op.plus.string_view]</strong> 
    <p>1) Let <code class="highlight"><c- n>S</c-></code> be <code class="highlight"><c- n>basic_string</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- p>,</c-> <c- n>Allocator</c-><c- o>></c-></code>; let <code class="highlight"><c- n>s</c-></code> be an instance of <code class="highlight"><c- n>S</c-></code>;
and let <code class="highlight"><c- n>SV</c-></code> be <code class="highlight"><c- n>basic_string_view</c-><c- o>&lt;</c-><c- n>charT</c-><c- p>,</c-> <c- n>traits</c-><c- o>></c-></code>.
Implementations shall provide sufficient additional overloads marked <code class="highlight"><c- k>constexpr</c-></code> so that an object <code class="highlight"><c- n>t</c-></code> with an implicit conversion to <code class="highlight"><c- n>SV</c-></code> can be
concatenated with <code class="highlight"><c- n>s</c-></code> according to Table �.</p>
    <p><i>Table �: Additional <code class="highlight"><c- n>basic_string</c-></code> concatenation overloads [tab:string.op.plus.string_view.overloads]</i></p>
    <table>
     <thead>
      <tr>
       <td>Expression
       <td>Equivalent to
     <tbody>
      <tr>
       <td><code class="highlight"><c- n>s</c-> <c- o>+</c-> <c- n>t</c-></code>
       <td><code class="highlight"><c- n>s</c-> <c- o>+</c-> <c- n>SV</c-><c- p>(</c-><c- n>t</c-><c- p>)</c-></code>
      <tr>
       <td><code class="highlight"><c- n>t</c-> <c- o>+</c-> <c- n>s</c-></code>
       <td><code class="highlight"><c- n>SV</c-><c- p>(</c-><c- n>t</c-><c- p>)</c-> <c- o>+</c-> <c- n>s</c-></code>
      <tr>
       <td><code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>move</c-><c- p>(</c-><c- n>s</c-><c- p>)</c-> <c- o>+</c-> <c- n>t</c-></code>
       <td><code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>move</c-><c- p>(</c-><c- n>s</c-><c- p>)</c-> <c- o>+</c-> <c- n>SV</c-><c- p>(</c-><c- n>t</c-><c- p>)</c-></code>
      <tr>
       <td><code class="highlight"><c- n>t</c-> <c- o>+</c-> <c- n>std</c-><c- o>::</c-><c- n>move</c-><c- p>(</c-><c- n>s</c-><c- p>)</c-></code>
       <td><code class="highlight"><c- n>SV</c-><c- p>(</c-><c- n>t</c-><c- p>)</c-> <c- o>+</c-> <c- n>std</c-><c- o>::</c-><c- n>move</c-><c- p>(</c-><c- n>s</c-><c- p>)</c-></code>
    </table>
   </blockquote>
   <hr>
   <p>In [diff], add a new subclause, tentatively named <strong>[diff.cpp26.strings]</strong>.</p>
   <p><strong>Note to the editor</strong>: such a subclause should be under [diff.cpp26],
which by the time this proposal is adopted, may or may not exist yet.
The naming and contents of the parent [diff.cpp26] subclause should
match the existing ones (e.g. [diff.cpp20]), of course adapted to C++26.</p>
   <blockquote> <strong>C.�.� [strings]: strings library [diff.cpp26.strings]</strong> </blockquote>
   <blockquote>
     (1) <strong>Affected subclause</strong>: [string.classes] 
    <p><strong>Change</strong>: Additional overloads of <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> between <code class="highlight"><c- n>basic_string</c-></code> specializations and types convertible to <code class="highlight"><c- n>basic_string_view</c-></code> specializations have been added.</p>
    <p><strong>Rationale</strong>: Make <code class="highlight"><c- k>operator</c-><c- o>+</c-></code> consistent with the existing overloads.</p>
    <p><strong>Effect on original feature</strong>: Valid C++23 code may fail to compile in
this revision of C++. For instance:</p>
<pre class="highlight"><c- k>template</c-> <c- o>&lt;</c-><c- k>typename</c-> <c- nc>Char</c-><c- o>></c->
<c- k>struct</c-> <c- nc>basic_my_string_view</c-><c- p>;</c->

<c- k>using</c-> <c- n>my_string_view</c-> <c- o>=</c-> <c- n>basic_my_string_view</c-><c- o>&lt;</c-><c- b>char</c-><c- o>></c-><c- p>;</c->

<c- k>template</c-> <c- o>&lt;</c-><c- k>typename</c-> <c- nc>Char</c-><c- o>></c->
  <c- n>std</c-><c- o>::</c-><c- n>basic_string</c-><c- o>&lt;</c-><c- n>Char</c-><c- o>></c->
    <c- k>operator</c-><c- o>+</c-><c- p>(</c-><c- n>std</c-><c- o>::</c-><c- n>basic_string</c-><c- o>&lt;</c-><c- n>Char</c-><c- o>></c-><c- p>,</c-> <c- n>std</c-><c- o>::</c-><c- n>type_identity_t</c-><c- o>&lt;</c-><c- n>basic_my_string_view</c-><c- o>&lt;</c-><c- n>Char</c-><c- o>>></c-><c- p>);</c->

<c- k>struct</c-> <c- nc>char_buffer</c->
<c- p>{</c->
  <c- k>operator</c-> <c- n>my_string_view</c-><c- p>()</c-> <c- k>const</c-><c- p>;</c->
  <c- k>operator</c-> <c- n>std</c-><c- o>::</c-><c- n>string_view</c-><c- p>()</c-> <c- k>const</c-><c- p>;</c->
<c- p>};</c->

<c- b>int</c-> <c- nf>main</c-><c- p>()</c-> <c- p>{</c->
  <c- n>char_buffer</c-> <c- n>buf</c-><c- p>;</c->
  <c- n>std</c-><c- o>::</c-><c- n>string</c-> <c- n>result</c-> <c- o>=</c-> <c- n>std</c-><c- o>::</c-><c- n>string</c-><c- p>(</c-><c- s>"hello"</c-><c- p>)</c-> <c- o>+</c-> <c- n>buf</c-><c- p>;</c-> <c- c1>// ill-formed (ambiguous); previously well-formed</c->
<c- p>}</c->
</pre>
   </blockquote>
   <h2 class="heading settled" data-level="7" id="acknowledgements"><span class="secno">7. </span><span class="content">Acknowledgements</span><a class="self-link" href="#acknowledgements"></a></h2>
   <p>Thanks to KDAB for supporting this work.</p>
   <p>Thanks to Will Hawkins for the discussions and the prototype implementation
in libc++.</p>
   <p>All remaining errors are ours and ours only.</p>
  </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-clazy-qstringbuilder">[Clazy-qstringbuilder]
   <dd><a href="https://github.com/KDE/clazy/blob/master/docs/checks/README-auto-unexpected-qstringbuilder.md"><cite>auto-unexpected-qstringbuilder</cite></a>. URL: <a href="https://github.com/KDE/clazy/blob/master/docs/checks/README-auto-unexpected-qstringbuilder.md">https://github.com/KDE/clazy/blob/master/docs/checks/README-auto-unexpected-qstringbuilder.md</a>
   <dt id="biblio-libcpp-string-concatenation">[Libcpp-string-concatenation]
   <dd><a href="https://github.com/llvm/llvm-project/blob/llvmorg-14.0.0/libcxx/include/string#L4218"><cite>operator+ between pointer and string in libc++</cite></a>. URL: <a href="https://github.com/llvm/llvm-project/blob/llvmorg-14.0.0/libcxx/include/string#L4218">https://github.com/llvm/llvm-project/blob/llvmorg-14.0.0/libcxx/include/string#L4218</a>
   <dt id="biblio-libstdcpp-string-concatenation">[Libstdcpp-string-concatenation]
   <dd><a href="https://github.com/gcc-mirror/gcc/blob/releases/gcc-12.1.0/libstdc%2B%2B-v3/include/bits/basic_string.tcc#L603"><cite>operator+ between pointer and string in libstdc++</cite></a>. URL: <a href="https://github.com/gcc-mirror/gcc/blob/releases/gcc-12.1.0/libstdc%2B%2B-v3/include/bits/basic_string.tcc#L603">https://github.com/gcc-mirror/gcc/blob/releases/gcc-12.1.0/libstdc%2B%2B-v3/include/bits/basic_string.tcc#L603</a>
   <dt id="biblio-lwg3950">[LWG3950]
   <dd>Giuseppe D'Angelo. <a href="https://wg21.link/lwg3950"><cite>std::basic_string_view comparison operators are overspecified</cite></a>. New. URL: <a href="https://wg21.link/lwg3950">https://wg21.link/lwg3950</a>
   <dt id="biblio-n3685">[N3685]
   <dd>Jeffrey Yasskin. <a href="https://wg21.link/n3685"><cite>string_view: a non-owning reference to a string, revision 4</cite></a>. 3 May 2013. URL: <a href="https://wg21.link/n3685">https://wg21.link/n3685</a>
   <dt id="biblio-n4950">[N4950]
   <dd>Thomas Köppe. <a href="https://wg21.link/n4950"><cite>Working Draft, Standard for Programming Language C++</cite></a>. 10 May 2023. URL: <a href="https://wg21.link/n4950">https://wg21.link/n4950</a>
   <dt id="biblio-p2591-gcc">[P2591-GCC]
   <dd>Giuseppe D'Angelo. <a href="https://github.com/dangelog/gcc/tree/P2591_string_view_concatenation"><cite>P2591 prototype implementation for libstdc++</cite></a>. URL: <a href="https://github.com/dangelog/gcc/tree/P2591_string_view_concatenation">https://github.com/dangelog/gcc/tree/P2591_string_view_concatenation</a>
   <dt id="biblio-p2591-llvm">[P2591-LLVM]
   <dd>Will Hawkins. <a href="https://github.com/hawkinsw/llvm-project/tree/P2591_string_view_concatenation"><cite>P2591 prototype implementation for libc++</cite></a>. URL: <a href="https://github.com/hawkinsw/llvm-project/tree/P2591_string_view_concatenation">https://github.com/hawkinsw/llvm-project/tree/P2591_string_view_concatenation</a>
   <dt id="biblio-qstringbuilder">[QStringBuilder]
   <dd><a href="https://doc.qt.io/qt-6/qstring.html#more-efficient-string-construction"><cite>QStringBuilder documentation</cite></a>. URL: <a href="https://doc.qt.io/qt-6/qstring.html#more-efficient-string-construction">https://doc.qt.io/qt-6/qstring.html#more-efficient-string-construction</a>
   <dt id="biblio-sd-8">[SD-8]
   <dd>Titus Winter. <a href="https://isocpp.org/std/standing-documents/sd-8-standard-library-compatibility"><cite>SD-8: Standard Library Compatibility</cite></a>. URL: <a href="https://isocpp.org/std/standing-documents/sd-8-standard-library-compatibility">https://isocpp.org/std/standing-documents/sd-8-standard-library-compatibility</a>
   <dt id="biblio-stackoverflow">[StackOverflow]
   <dd><a href="https://stackoverflow.com/questions/44636549/why-is-there-no-support-for-concatenating-stdstring-and-stdstring-view"><cite>Why is there no support for concatenating std::string and std::string_view?</cite></a>. URL: <a href="https://stackoverflow.com/questions/44636549/why-is-there-no-support-for-concatenating-stdstring-and-stdstring-view">https://stackoverflow.com/questions/44636549/why-is-there-no-support-for-concatenating-stdstring-and-stdstring-view</a>
  </dl>