<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="mpark/wg21" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <meta name="dcterms.date" content="2025-03-14" />
  <title>Future-proof submdspan_mapping?</title>
  <style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
</style>
  <style>
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ background-color: #f6f8fa; }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span { } 
code span.al { color: #ff0000; } 
code span.an { } 
code span.at { } 
code span.bn { color: #9f6807; } 
code span.bu { color: #9f6807; } 
code span.cf { color: #00607c; } 
code span.ch { color: #9f6807; } 
code span.cn { } 
code span.co { color: #008000; font-style: italic; } 
code span.cv { color: #008000; font-style: italic; } 
code span.do { color: #008000; } 
code span.dt { color: #00607c; } 
code span.dv { color: #9f6807; } 
code span.er { color: #ff0000; font-weight: bold; } 
code span.ex { } 
code span.fl { color: #9f6807; } 
code span.fu { } 
code span.im { } 
code span.in { color: #008000; } 
code span.kw { color: #00607c; } 
code span.op { color: #af1915; } 
code span.ot { } 
code span.pp { color: #6f4e37; } 
code span.re { } 
code span.sc { color: #9f6807; } 
code span.ss { color: #9f6807; } 
code span.st { color: #9f6807; } 
code span.va { } 
code span.vs { color: #9f6807; } 
code span.wa { color: #008000; font-weight: bold; } 
code.diff {color: #898887}
code.diff span.va {color: #00AA00}
code.diff span.st {color: #bf0303}
</style>
  <style type="text/css">
body {
margin: 5em;
font-family: serif;

hyphens: auto;
line-height: 1.35;
}
div.wrapper {
max-width: 60em;
margin: auto;
}
ul {
list-style-type: none;
padding-left: 2em;
margin-top: -0.2em;
margin-bottom: -0.2em;
}
a {
text-decoration: none;
color: #4183C4;
}
a.hidden_link {
text-decoration: none;
color: inherit;
}
li {
margin-top: 0.6em;
margin-bottom: 0.6em;
}
h1, h2, h3, h4 {
position: relative;
line-height: 1;
}
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;
font-family: sans-serif;
font-weight: normal;
font-size: 83%;
}
a.self-link:hover { opacity: 1; }
a.self-link::before { content: "§"; }
ul > li:before {
content: "\2014";
position: absolute;
margin-left: -1.5em;
}
:target { background-color: #C9FBC9; }
:target .codeblock { background-color: #C9FBC9; }
:target ul { background-color: #C9FBC9; }
.abbr_ref { float: right; }
.folded_abbr_ref { float: right; }
:target .folded_abbr_ref { display: none; }
:target .unfolded_abbr_ref { float: right; display: inherit; }
.unfolded_abbr_ref { display: none; }
.secnum { display: inline-block; min-width: 35pt; }
.header-section-number { display: inline-block; min-width: 35pt; }
.annexnum { display: block; }
div.sourceLinkParent {
float: right;
}
a.sourceLink {
position: absolute;
opacity: 0;
margin-left: 10pt;
}
a.sourceLink:hover {
opacity: 1;
}
a.itemDeclLink {
position: absolute;
font-size: 75%;
text-align: right;
width: 5em;
opacity: 0;
}
a.itemDeclLink:hover { opacity: 1; }
span.marginalizedparent {
position: relative;
left: -5em;
}
li span.marginalizedparent { left: -7em; }
li ul > li span.marginalizedparent { left: -9em; }
li ul > li ul > li span.marginalizedparent { left: -11em; }
li ul > li ul > li ul > li span.marginalizedparent { left: -13em; }
div.footnoteNumberParent {
position: relative;
left: -4.7em;
}
a.marginalized {
position: absolute;
font-size: 75%;
text-align: right;
width: 5em;
}
a.enumerated_item_num {
position: relative;
left: -3.5em;
display: inline-block;
margin-right: -3em;
text-align: right;
width: 3em;
}
div.para { margin-bottom: 0.6em; margin-top: 0.6em; text-align: justify; }
div.section { text-align: justify; }
div.sentence { display: inline; }
span.indexparent {
display: inline;
position: relative;
float: right;
right: -1em;
}
a.index {
position: absolute;
display: none;
}
a.index:before { content: "⟵"; }

a.index:target {
display: inline;
}
.indexitems {
margin-left: 2em;
text-indent: -2em;
}
div.itemdescr {
margin-left: 3em;
}
.bnf {
font-family: serif;
margin-left: 40pt;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.ncbnf {
font-family: serif;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: 40pt;
}
.ncsimplebnf {
font-family: serif;
font-style: italic;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: 40pt;
background: inherit; 
}
span.textnormal {
font-style: normal;
font-family: serif;
white-space: normal;
display: inline-block;
}
span.rlap {
display: inline-block;
width: 0px;
}
span.descr { font-style: normal; font-family: serif; }
span.grammarterm { font-style: italic; }
span.term { font-style: italic; }
span.terminal { font-family: monospace; font-style: normal; }
span.nonterminal { font-style: italic; }
span.tcode { font-family: monospace; font-style: normal; }
span.textbf { font-weight: bold; }
span.textsc { font-variant: small-caps; }
a.nontermdef { font-style: italic; font-family: serif; }
span.emph { font-style: italic; }
span.techterm { font-style: italic; }
span.mathit { font-style: italic; }
span.mathsf { font-family: sans-serif; }
span.mathrm { font-family: serif; font-style: normal; }
span.textrm { font-family: serif; }
span.textsl { font-style: italic; }
span.mathtt { font-family: monospace; font-style: normal; }
span.mbox { font-family: serif; font-style: normal; }
span.ungap { display: inline-block; width: 2pt; }
span.textit { font-style: italic; }
span.texttt { font-family: monospace; }
span.tcode_in_codeblock { font-family: monospace; font-style: normal; }
span.phantom { color: white; }

span.math { font-style: normal; }
span.mathblock {
display: block;
margin-left: auto;
margin-right: auto;
margin-top: 1.2em;
margin-bottom: 1.2em;
text-align: center;
}
span.mathalpha {
font-style: italic;
}
span.synopsis {
font-weight: bold;
margin-top: 0.5em;
display: block;
}
span.definition {
font-weight: bold;
display: block;
}
.codeblock {
margin-left: 1.2em;
line-height: 127%;
}
.outputblock {
margin-left: 1.2em;
line-height: 127%;
}
div.itemdecl {
margin-top: 2ex;
}
code.itemdeclcode {
white-space: pre;
display: block;
}
span.textsuperscript {
vertical-align: super;
font-size: smaller;
line-height: 0;
}
.footnotenum { vertical-align: super; font-size: smaller; line-height: 0; }
.footnote {
font-size: small;
margin-left: 2em;
margin-right: 2em;
margin-top: 0.6em;
margin-bottom: 0.6em;
}
div.minipage {
display: inline-block;
margin-right: 3em;
}
div.numberedTable {
text-align: center;
margin: 2em;
}
div.figure {
text-align: center;
margin: 2em;
}
table {
border: 1px solid black;
border-collapse: collapse;
margin-left: auto;
margin-right: auto;
margin-top: 0.8em;
text-align: left;
hyphens: none; 
}
td, th {
padding-left: 1em;
padding-right: 1em;
vertical-align: top;
}
td.empty {
padding: 0px;
padding-left: 1px;
}
td.left {
text-align: left;
}
td.right {
text-align: right;
}
td.center {
text-align: center;
}
td.justify {
text-align: justify;
}
td.border {
border-left: 1px solid black;
}
tr.rowsep, td.cline {
border-top: 1px solid black;
}
tr.even, tr.odd {
border-bottom: 1px solid black;
}
tr.capsep {
border-top: 3px solid black;
border-top-style: double;
}
tr.header {
border-bottom: 3px solid black;
border-bottom-style: double;
}
th {
border-bottom: 1px solid black;
}
span.centry {
font-weight: bold;
}
div.table {
display: block;
margin-left: auto;
margin-right: auto;
text-align: center;
width: 90%;
}
span.indented {
display: block;
margin-left: 2em;
margin-bottom: 1em;
margin-top: 1em;
}
ol.enumeratea { list-style-type: none; background: inherit; }
ol.enumerate { list-style-type: none; background: inherit; }

code.sourceCode > span { display: inline; }

div#refs p { padding-left: 32px; text-indent: -32px; }
</style>
  <link href="data:image/vnd.microsoft.icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAVoJEAN6CRADegkQAWIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wCCRAAAgkQAAIJEAACCRAAsgkQAvoJEAP+CRAD/gkQA/4JEAP+CRADAgkQALoJEAACCRAAAgkQAAP///wD///8AgkQAAIJEABSCRACSgkQA/IJEAP99PQD/dzMA/3czAP99PQD/gkQA/4JEAPyCRACUgkQAFIJEAAD///8A////AHw+AFiBQwDqgkQA/4BBAP9/PxP/uZd6/9rJtf/bybX/upd7/39AFP+AQQD/gkQA/4FDAOqAQgBc////AP///wDKklv4jlEa/3o7AP+PWC//8+3o///////////////////////z7un/kFox/35AAP+GRwD/mVYA+v///wD///8A0Zpk+NmibP+0d0T/8evj///////+/fv/1sKz/9bCs//9/fr//////+/m2/+NRwL/nloA/5xYAPj///8A////ANKaZPjRmGH/5cKh////////////k149/3UwAP91MQD/lmQ//86rhv+USg3/m1YA/5hSAP+bVgD4////AP///wDSmmT4zpJY/+/bx///////8+TV/8mLT/+TVx//gkIA/5lVAP+VTAD/x6B//7aEVv/JpH7/s39J+P///wD///8A0ppk+M6SWP/u2sf///////Pj1f/Nj1T/2KFs/8mOUv+eWhD/lEsA/8aee/+0glT/x6F7/7J8Rvj///8A////ANKaZPjRmGH/48Cf///////+/v7/2qt//82PVP/OkFX/37KJ/86siv+USg7/mVQA/5hRAP+bVgD4////AP///wDSmmT40ppk/9CVXP/69O////////7+/v/x4M//8d/P//7+/f//////9u7n/6tnJf+XUgD/nFgA+P///wD///8A0ppk+NKaZP/RmWL/1qNy//r07///////////////////////+vXw/9akdP/Wnmn/y5FY/6JfFvj///8A////ANKaZFTSmmTo0ppk/9GYYv/Ql1//5cWm//Hg0P/x4ND/5cWm/9GXYP/RmGH/0ppk/9KaZOjVnmpY////AP///wDSmmQA0ppkEtKaZI7SmmT60ppk/9CWX//OkVb/zpFW/9CWX//SmmT/0ppk/NKaZJDSmmQS0ppkAP///wD///8A0ppkANKaZADSmmQA0ppkKtKaZLrSmmT/0ppk/9KaZP/SmmT/0ppkvNKaZCrSmmQA0ppkANKaZAD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkUtKaZNzSmmTc0ppkVNKaZADSmmQA0ppkANKaZADSmmQA////AP5/AAD4HwAA4AcAAMADAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAMADAADgBwAA+B8AAP5/AAAoAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAyCRACMgkQA6oJEAOqCRACQgkQAEIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRABigkQA5oJEAP+CRAD/gkQA/4JEAP+CRADqgkQAZoJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAA4gkQAwoJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQAxIJEADyCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAP///wD///8A////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAWgkQAmIJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAJyCRAAYgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAdIJEAPCCRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAPSCRAB4gkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQASoJEANKCRAD/gkQA/4JEAP+CRAD/g0YA/39AAP9zLgD/bSQA/2shAP9rIQD/bSQA/3MuAP9/PwD/g0YA/4JEAP+CRAD/gkQA/4JEAP+CRADUgkQAToJEAACCRAAAgkQAAP///wD///8A////AP///wB+PwAAgkUAIoJEAKiCRAD/gkQA/4JEAP+CRAD/hEcA/4BBAP9sIwD/dTAA/5RfKv+viF7/vp56/76ee/+wiF7/lWAr/3YxAP9sIwD/f0AA/4RHAP+CRAD/gkQA/4JEAP+CRAD/gkQArIJEACaBQwAA////AP///wD///8A////AIBCAEBzNAD6f0EA/4NFAP+CRAD/gkQA/4VIAP92MwD/bSUA/6N1Tv/ezsL/////////////////////////////////38/D/6V3Uv9uJgD/dTEA/4VJAP+CRAD/gkQA/4JEAP+BQwD/fUAA/4FDAEj///8A////AP///wD///8AzJRd5qBlKf91NgD/dDUA/4JEAP+FSQD/cy4A/3YyAP/PuKP//////////////////////////////////////////////////////9K7qP94NQD/ciwA/4VJAP+CRAD/fkEA/35BAP+LSwD/mlYA6v///wD///8A////AP///wDdpnL/4qx3/8KJUv+PUhf/cTMA/3AsAP90LgD/4dK+/////////////////////////////////////////////////////////////////+TYxf91MAD/dTIA/31CAP+GRwD/llQA/6FcAP+gWwD8////AP///wD///8A////ANGZY/LSm2X/4ap3/92mcP+wdT3/byQA/8mwj////////////////////////////////////////////////////////////////////////////+LYxv9zLgP/jUoA/59bAP+hXAD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/RmWL/1p9q/9ubXv/XqXj////////////////////////////7+fD/vZyG/6BxS/+gcUr/vJuE//r37f//////////////////////3MOr/5dQBf+dVQD/nVkA/5xYAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmWP/yohJ//jo2P//////////////////////4NTG/4JDFf9lGAD/bSQA/20kAP9kGAD/fz8S/+Xb0f//////5NG9/6txN/+LOgD/m1QA/51aAP+cWAD/m1cA/5xYAP+cWADy////AP///wD///8A////ANKaZPLSmmT/0ppk/8+TWf/Unmv//v37//////////////////////+TWRr/VwsA/35AAP+ERgD/g0UA/4JGAP9lHgD/kFga/8KXX/+TRwD/jT4A/49CAP+VTQD/n10A/5xYAP+OQQD/lk4A/55cAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/y4tO/92yiP//////////////////////8NnE/8eCQP+rcTT/ez0A/3IyAP98PgD/gEMA/5FSAP+USwD/jj8A/5lUAP+JNwD/yqV2/694Mf+HNQD/jkAA/82rf/+laBj/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/LiUr/4byY///////////////////////gupX/0I5P/+Wuev/Lklz/l1sj/308AP+QSwD/ol0A/59aAP+aVQD/k0oA/8yoh///////+fXv/6pwO//Lp3v///////Pr4f+oay7y////AP///wD///8A////ANKaZPLSmmT/0ppk/8uJSv/hvJj//////////////////////+G7l//Jhkb/0ppk/96nc//fqXX/x4xO/6dkFP+QSQD/llEA/5xXAP+USgD/yaOA///////38uv/qG05/8ijdv//////8efb/6ZpLPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/zIxO/9yxh///////////////////////7dbA/8iEQf/Sm2X/0Zlj/9ScZv/eqHf/2KJv/7yAQf+XTgD/iToA/5lSAP+JNgD/yKFv/611LP+HNQD/jT8A/8qmeP+kZRT/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/Pk1n/1J5q//78+//////////////////+/fv/1aFv/8iEQv/Tm2b/0ppl/9GZY//Wn2z/1pZc/9eldf/Bl2b/kUcA/4w9AP+OQAD/lUwA/59eAP+cWQD/jT8A/5ZOAP+eXADy////AP///wD///8A////ANKaZPLSmmT/0ppk/9KZY//KiEn/8d/P///////////////////////47+f/05tm/8iCP//KiEj/yohJ/8eCP//RmGH//vfy///////n1sP/rXQ7/4k4AP+TTAD/nVoA/5xYAP+cVwD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/0ptl/8uLTf/aq37////////////////////////////+/fz/6c2y/961jv/etY7/6Myx//78+v//////////////////////3MWv/5xXD/+ORAD/mFQA/51ZAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmmT/0ppk/8mFRP/s1b//////////////////////////////////////////////////////////////////////////////+PD/0JFU/7NzMv+WUQD/kUsA/5tXAP+dWQDy////AP///wD///8A////ANKaZP/SmmT/0ppk/9KaZP/Sm2X/z5NZ/8yMT//z5NX/////////////////////////////////////////////////////////////////9Ofa/8yNUP/UmGH/36p5/8yTWv+qaSD/kksA/5ROAPz///8A////AP///wD///8A0ppk5NKaZP/SmmT/0ppk/9KaZP/TnGf/zY9T/82OUv/t1sD//////////////////////////////////////////////////////+7Yw//OkFX/zI5R/9OcZ//SmmP/26V0/9ymdf/BhUf/ol8R6P///wD///8A////AP///wDSmmQ80ppk9tKaZP/SmmT/0ppk/9KaZP/TnGj/zpFW/8qJSv/dson/8uHS//////////////////////////////////Lj0//etIv/y4lL/86QVf/TnGj/0ppk/9KaZP/RmWP/05xn/9ymdfjUnWdC////AP///wD///8A////ANKaZADSmmQc0ppkotKaZP/SmmT/0ppk/9KaZP/Tm2b/0Zli/8qJSf/NjlH/16Z3/+G8mP/myKr/5siq/+G8mP/Xp3f/zY5S/8qISf/RmGH/05tm/9KaZP/SmmT/0ppk/9KaZP/SmmSm0pljINWdaQD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkQtKaZMrSmmT/0ppk/9KaZP/SmmT/0ptl/9GYYf/Nj1P/y4lL/8qISP/KiEj/y4lK/82PU//RmGH/0ptl/9KaZP/SmmT/0ppk/9KaZP/SmmTO0ppkRtKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZGzSmmTu0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmTw0ppkcNKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZBLSmmSQ0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppklNKaZBTSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQy0ppkutKaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppkvtKaZDbSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkXNKaZODSmmT/0ppk/9KaZP/SmmT/0ppk5NKaZGDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkBtKaZIbSmmTo0ppk6tKaZIrSmmQK0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP/8P///+B///+AH//+AAf//AAD//AAAP/AAAA/gAAAHwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA+AAAAfwAAAP/AAAP/8AAP//gAH//+AH///4H////D//" rel="icon" />
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->
  
</head>
<body>
<div class="wrapper">
<header id="title-block-header">
<h1 class="title" style="text-align:center">Future-proof
<code>submdspan_mapping</code>?</h1>

<table style="border:none;float:right">
  <tr>
    <td>Document #: </td>
    <td>P3663R0</td>
  </tr>
  <tr>
    <td>Date: </td>
    <td>2025-03-14</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Project: </td>
    <td>Programming Language C++<br>
      LEWG<br>
    </td>
  </tr>
  <tr>
    <td style="vertical-align:top">Reply-to: </td>
    <td>
      Mark Hoemmen<br>&lt;<a href="mailto:mhoemmen@nvidia.com" class="email">mhoemmen@nvidia.com</a>&gt;<br>
    </td>
  </tr>
</table>

</header>
<div style="clear:both">
<div id="TOC" role="doc-toc">
<h1 id="toctitle">Contents</h1>
<ul>
<li><a href="#author" id="toc-author"><span class="toc-section-number">1</span> Author</a></li>
<li><a href="#revision-history" id="toc-revision-history"><span class="toc-section-number">2</span> Revision history</a></li>
<li><a href="#abstract" id="toc-abstract"><span class="toc-section-number">3</span> Abstract</a></li>
<li><a href="#motivation" id="toc-motivation"><span class="toc-section-number">4</span> Motivation</a>
<ul>
<li><a href="#summary" id="toc-summary"><span class="toc-section-number">4.1</span> Summary</a></li>
<li><a href="#four-categories-of-slice-types" id="toc-four-categories-of-slice-types"><span class="toc-section-number">4.2</span> Four categories of slice
types</a></li>
<li><a href="#why-undefined-behavior" id="toc-why-undefined-behavior"><span class="toc-section-number">4.3</span> Why undefined behavior?</a></li>
<li><a href="#set-of-contiguous-subrange-slices-was-expanded-once-and-may-be-again" id="toc-set-of-contiguous-subrange-slices-was-expanded-once-and-may-be-again"><span class="toc-section-number">4.4</span> Set of contiguous subrange slices
was expanded once and may be again</a></li>
<li><a href="#other-existing-categories-could-be-expanded" id="toc-other-existing-categories-could-be-expanded"><span class="toc-section-number">4.5</span> Other existing categories could be
expanded</a></li>
<li><a href="#wg21-may-add-a-new-slice-category" id="toc-wg21-may-add-a-new-slice-category"><span class="toc-section-number">4.6</span> WG21 may add a new slice
category</a></li>
</ul></li>
<li><a href="#possible-solution-make-submdspan-canonicalize-slices" id="toc-possible-solution-make-submdspan-canonicalize-slices"><span class="toc-section-number">5</span> Possible solution: Make
<code>submdspan</code> canonicalize slices</a>
<ul>
<li><a href="#summary-1" id="toc-summary-1"><span class="toc-section-number">5.1</span> Summary</a></li>
<li><a href="#canonicalization-rules" id="toc-canonicalization-rules"><span class="toc-section-number">5.2</span> Canonicalization rules</a></li>
<li><a href="#how-to-apply-canonicalization" id="toc-how-to-apply-canonicalization"><span class="toc-section-number">5.3</span> How to apply
canonicalization</a></li>
<li><a href="#what-about-precondition-checking" id="toc-what-about-precondition-checking"><span class="toc-section-number">5.4</span> What about precondition
checking?</a></li>
<li><a href="#what-about-performance" id="toc-what-about-performance"><span class="toc-section-number">5.5</span> What about performance?</a></li>
<li><a href="#hypothetical-performance-mitigations" id="toc-hypothetical-performance-mitigations"><span class="toc-section-number">5.6</span> Hypothetical performance
mitigations</a></li>
<li><a href="#implementation" id="toc-implementation"><span class="toc-section-number">5.7</span> Implementation</a></li>
<li><a href="#wording-for-this-solution" id="toc-wording-for-this-solution"><span class="toc-section-number">5.8</span> Wording for this solution</a>
<ul>
<li><a href="#increment-__cpp_lib_submdspan-feature-test-macro" id="toc-increment-__cpp_lib_submdspan-feature-test-macro"><span class="toc-section-number">5.8.1</span> Increment
<code>__cpp_lib_submdspan</code> feature test macro</a></li>
<li><a href="#change-mdspan.sub.helpers" id="toc-change-mdspan.sub.helpers"><span class="toc-section-number">5.8.2</span> Change
[mdspan.sub.helpers]</a></li>
<li><a href="#requirements-of-all-submdspan_mapping-customizations" id="toc-requirements-of-all-submdspan_mapping-customizations"><span class="toc-section-number">5.8.3</span> Requirements of all
<code>submdspan_mapping</code> customizations</a></li>
<li><a href="#submdspan-function-template" id="toc-submdspan-function-template"><span class="toc-section-number">5.8.4</span> <code>submdspan</code> function
template</a></li>
</ul></li>
<li><a href="#why-we-do-not-propose-this-solution" id="toc-why-we-do-not-propose-this-solution"><span class="toc-section-number">5.9</span> Why we do not propose this
solution</a></li>
</ul></li>
<li><a href="#other-solutions" id="toc-other-solutions"><span class="toc-section-number">6</span> Other solutions</a>
<ul>
<li><a href="#make-p2769s-submdspan-changes-a-dr-for-c26" id="toc-make-p2769s-submdspan-changes-a-dr-for-c26"><span class="toc-section-number">6.1</span> Make P2769’s
<code>submdspan</code> changes a DR for C++26</a></li>
</ul></li>
<li><a href="#acknowledgements" id="toc-acknowledgements"><span class="toc-section-number">7</span> Acknowledgements</a></li>
</ul>
</div>
<h1 data-number="1" id="author"><span class="header-section-number">1</span> Author<a href="#author" class="self-link"></a></h1>
<ul>
<li>Mark Hoemmen (mhoemmen@nvidia.com) (NVIDIA)</li>
</ul>
<h1 data-number="2" id="revision-history"><span class="header-section-number">2</span> Revision history<a href="#revision-history" class="self-link"></a></h1>
<ul>
<li>Revision 0 to be submitted 2025-03-17</li>
</ul>
<h1 data-number="3" id="abstract"><span class="header-section-number">3</span> Abstract<a href="#abstract" class="self-link"></a></h1>
<p>This proposal apprises WG21 of an issue with
<code>submdspan_mapping</code>. We do not think the issue warrants a
change to C++26. However, we present a solution to be applied to C++26,
in case WG21 thinks a change is needed.</p>
<p>Currently, the <code>submdspan</code> function can call a
<code>submdspan_mapping</code> customization with any valid slice type.
This means that <code>submdspan_mapping</code> customizations may be
ill-formed, possibly even without a diagnosic, if future C++ versions
expand the set of valid slice types. This will happen if <a href="https://wg21.link/p2769r3">P2769R3</a> is merged into the Working
Draft for C++29, as that would generalize
<em><code>tuple-like</code></em> from a concrete list of types to a
concept. It may also happen in the future for other categories of slice
types.</p>
<p>The natural way to fix this is with a “canonicalization” approach.
First, define a fixed set of “canonical” slice types that represent all
valid slices, and a function to map valid slices to their canonical
versions. Then, change <code>submdspan</code> so it always canonicalizes
its input slices, and so it only ever calls
<code>submdspan_mapping</code> customizations with canonical slices.</p>
<p>We do not propose this as a change for C++26 because we believe that
this is something implementations would want to do anyway. The
possibility of later generalization of <em><code>tuple-like</code></em>
would naturally lead high-quality C++26 implementations to canonicalize
such slice inputs. That would minimize code changes both for users, and
for implementations (as the Standard Library currently has five
<code>submdspan_mapping</code> customizations of its own). Nevertheless,
we present a draft of a canonicalization design as an option for WG21 to
consider.</p>
<h1 data-number="4" id="motivation"><span class="header-section-number">4</span> Motivation<a href="#motivation" class="self-link"></a></h1>
<h2 data-number="4.1" id="summary"><span class="header-section-number">4.1</span> Summary<a href="#summary" class="self-link"></a></h2>
<p>The current <code>submdspan</code> wording has the following
constraint (<a href="https://eel.is/c++draft/mdspan.sub.sub#3.2">[mdspan.sub.sub]
3.2</a>).</p>
<blockquote>
<p>… the expression
<code>submdspan_mapping(src.mapping(), slices...)</code> is well-formed
when treated as an unevaluated operand.</p>
</blockquote>
<p>However, nothing currently requires <code>submdspan_mapping</code> to
be ill-formed when any of its slices is <em>not</em> a valid slice type.
Thus, the <code>submdspan</code> function can call a
<code>submdspan_mapping</code> customization with any slice type that is
valid for <code>submdspan</code>. This means that the following scenario
invokes undefined behavior.</p>
<ol type="1">
<li><p>A user defines a layout mapping <code>my_layout::mapping</code>
with a C++26 - compatible <code>submdspan_mapping</code>
customization.</p></li>
<li><p>A later C++ version adds a new valid slice type
<code>new_slice</code>.</p></li>
<li><p><code>submdspan</code> is called with
<code>my_layout::mapping</code> and a <code>new_slice</code>
slice.</p></li>
</ol>
<p>This is true whether the new slice type is in one of the existing
four categories of slice types or is in a new category. It is a scenario
that has already occurred and may occur again, as we will explain
below.</p>
<h2 data-number="4.2" id="four-categories-of-slice-types"><span class="header-section-number">4.2</span> Four categories of slice
types<a href="#four-categories-of-slice-types" class="self-link"></a></h2>
<p>The Working Draft has four categories of slice types. Let
<code>S</code> be an input slice type of <code>submdspan</code>.</p>
<ol type="1">
<li><p>“Full”: <code>S</code> is convertible to
<code>full_extent_t</code>. This means “all of the indices in that
extent.”</p></li>
<li><p>“Contiguous subrange”: <code>S</code> models
<em><code>index-pair-like</code></em><code>&lt;index_type&gt;</code>, or
<code>S</code> is a <code>strided_slice</code> specialization whose
<code>stride_type</code> is <em><code>integral-constant-like</code></em>
with value 1. A contiguous subrange slice represents a contiguous range
of indices in that extent. The fact that the indices are contiguous (in
other words, that they have stride 1) is known at compile time as a
function of the slice’s type alone. The special case of
<code>strided_slice</code> with compile-time stride 1 is the only way to
represent a contiguous index range with run-time offset but compile-time
extent. (See <a href="https://wg21.link/p3355r2">P3355r2</a>, which was
voted into the Working Draft for C++26 at the 2024 Wrocław
meeting.)</p></li>
<li><p>“Strided”: <code>S</code> is a specialization of
<code>strided_slice</code>, but is not a contiguous subrange. It may
represent a noncontiguous set of indices in that extent.</p></li>
<li><p>“Integer”: <code>S</code> is convertible to (the layout
mapping’s) <code>index_type</code>. This means “fix the slice to view
that extent at only that index.” Each integer slice reduces the result’s
rank by one.</p></li>
</ol>
<p>The Draft uses the term <em>unit-stride slice</em> to include both
full and contiguous subrange slices.</p>
<h2 data-number="4.3" id="why-undefined-behavior"><span class="header-section-number">4.3</span> Why undefined behavior?<a href="#why-undefined-behavior" class="self-link"></a></h2>
<p>Why would calling a C++26 - compatible <code>submdspan_mapping</code>
customization with a post - C++26 slice type be undefined behavior? Why
wouldn’t it just be ill-formed? The problem is that the customization
might actually be well-formed for new slice types, but it might do
something that <code>submdspan</code> doesn’t expect. For example, it
might violate preconditions of the <code>submdspan_mapping</code>
customization, or it might produce a
<code>submdspan_mapping_result</code> that violates postconditions of
<code>submdspan</code> (e.g., views the wrong elements). This could
happen for two reasons.</p>
<ol type="1">
<li><p>The user has overloaded the customization incorrectly. It works
correctly for all C++26 slice types, but incorrectly maps the new slice
type to one of the four C++26 cases.</p></li>
<li><p>The user intends for the customization to be well-formed with
invalid slice types. It might have behavior that makes sense for the
user, but not for <code>submdspan</code>, as in the following
example.</p></li>
</ol>
<div class="sourceCode" id="cb1"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> my_tag_slice_t <span class="op">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="dt">size_t</span> k<span class="op">&gt;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> <span class="kw">constexpr</span> <span class="dt">size_t</span> get<span class="op">()</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span>k <span class="op">==</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> <span class="dv">42</span>;</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span> <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span>k <span class="op">==</span> <span class="dv">1</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> <span class="dv">100</span>;</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>      <span class="kw">static_assert</span><span class="op">(</span><span class="kw">false</span><span class="op">)</span>;</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> std <span class="op">{</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> tuple_size<span class="op">&lt;</span>my_tag_slice_t<span class="op">&gt;</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a>    <span class="op">:</span> integral_constant<span class="op">&lt;</span><span class="dt">size_t</span>, <span class="dv">2</span><span class="op">&gt;</span> <span class="op">{}</span>;</span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="dt">size_t</span> I<span class="op">&gt;</span> </span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> tuple_element<span class="op">&lt;</span>I, my_tag_slice_t<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> type <span class="op">=</span> <span class="dt">size_t</span>;</span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> <span class="co">// namespace std</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span><span class="op">...</span> Slices<span class="op">&gt;</span></span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a><span class="kw">friend</span> <span class="kw">constexpr</span> <span class="kw">auto</span></span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a>  submdspan_mapping<span class="op">(</span><span class="kw">const</span> mapping<span class="op">&amp;</span> src, Slices<span class="op">...</span> slices<span class="op">)</span></span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">((</span>valid_cpp26_slice<span class="op">&lt;</span>Slices<span class="op">&gt;</span> <span class="op">&amp;&amp;</span> <span class="op">...))</span> <span class="op">{</span></span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ... all slices are valid C++26 slices,</span></span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a>    <span class="co">// so do the normal C++26 thing ...</span></span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a>  <span class="cf">else</span> <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span>is_same_v<span class="op">&lt;</span>Slices, my_tag_slice_t<span class="op">&gt;</span> <span class="op">&amp;&amp;</span> <span class="op">...)</span> <span class="op">{</span></span>
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a>    <span class="co">// my_tag_slice_t is not a valid C++26 slice,</span></span>
<span id="cb1-35"><a href="#cb1-35" aria-hidden="true" tabindex="-1"></a>    <span class="co">// but it has an unambiguous interpretation</span></span>
<span id="cb1-36"><a href="#cb1-36" aria-hidden="true" tabindex="-1"></a>    <span class="co">// as a pair of indices.</span></span>
<span id="cb1-37"><a href="#cb1-37" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> std<span class="op">::</span>string<span class="op">(</span><span class="st">&quot;Hello, I am doing something weird&quot;</span><span class="op">)</span>;</span>
<span id="cb1-38"><a href="#cb1-38" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb1-39"><a href="#cb1-39" aria-hidden="true" tabindex="-1"></a>  <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb1-40"><a href="#cb1-40" aria-hidden="true" tabindex="-1"></a>    <span class="kw">static_assert</span><span class="op">(</span><span class="kw">false</span><span class="op">)</span>;</span>
<span id="cb1-41"><a href="#cb1-41" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb1-42"><a href="#cb1-42" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h2 data-number="4.4" id="set-of-contiguous-subrange-slices-was-expanded-once-and-may-be-again"><span class="header-section-number">4.4</span> Set of contiguous subrange
slices was expanded once and may be again<a href="#set-of-contiguous-subrange-slices-was-expanded-once-and-may-be-again" class="self-link"></a></h2>
<p>WG21 has already expanded the set of contiguous subrange slice types
once, and is likely to do so again. This is because it depends on the
exposition-only concept <em><code>pair-like</code></em>. Adoption of <a href="https://wg21.link/p2819r2">P2819R2</a> into the Working Draft for
C++26 at the 2024 Tokyo meeting made <code>complex</code> a
<em><code>pair-like</code></em> type, and therefore a valid slice type
for <code>submdspan</code>. <a href="https://wg21.link/p2769r3">P2769R3</a>, currently in LEWG review,
proposes generalizing <em><code>tuple-like</code></em> from a concrete
list of types to a concept. That would make user-defined pair types
valid slice types.</p>
<p>Before adoption of <a href="https://wg21.link/p2819r2">P2819R2</a>, a
user-defined layout mapping’s <code>submdspan_mapping</code>
customization could determine whether a type is <code>T</code> is
<em><code>pair-like</code></em> by testing exhaustively whether it is
<code>array&lt;X, 2&gt;</code> for some <code>X</code>,
<code>tuple&lt;X, Y&gt;</code> or <code>pair&lt;X, Y&gt;</code> for some
<code>X</code> and <code>Y</code>, or <code>ranges::subrange</code> with
<code>tuple_size</code> 2. P2819 would break a customization that uses
this approach. Similarly, adoption of <a href="https://wg21.link/p2769r3">P2769R3</a> would break customizations
that add <code>complex&lt;R&gt;</code> (for some <code>R</code>) to this
list of types to test exhaustively.</p>
<p>One could argue that any type <code>T</code> with <code>get</code>
findable by argument-dependent lookup (ADL),
<code>tuple_size&lt;T&gt;</code> of 2, and
<code>tuple_element&lt;I, T&gt;</code> convertible to
<code>index_type</code> for all <code>I</code> has an unambiguous
interpretation as a contiguous subrange. Thus, users should write their
customizations generically to that interface. A reasonable way to do
that would be to imitate the current wording that uses
<code>get&lt;0&gt;</code> (<a href="https://eel.is/c++draft/views.multidim#mdspan.sub.helpers-2.2">[mdspan.sub.helpers]
2.2</a>) and <code>get&lt;1&gt;</code> (<a href="https://eel.is/c++draft/views.multidim#mdspan.sub.helpers-7.2">[mdspan.sub.helpers]
7.2</a>). This is a reasonable thing to do. However, in terms of the
Standard, there are three issues with that approach.</p>
<ol type="1">
<li><p>Standard Library implementers need to assume that users read the
Standard as narrowly as possible. C++26 does not forbid calling a
<code>submdspan_mapping</code> customization with a user-defined pair
type, but it also does not restrict its behavior in that case.</p></li>
<li><p>What if a future C++ version calls any type destructurable into
two elements a “pair-like type,” even if it does not have ADL-findable
<code>get</code>?</p></li>
<li><p>This approach would not help if other slice categories are
expanded, or if a new slice category is added, as the following sections
explain.</p></li>
</ol>
<h2 data-number="4.5" id="other-existing-categories-could-be-expanded"><span class="header-section-number">4.5</span> Other existing categories could
be expanded<a href="#other-existing-categories-could-be-expanded" class="self-link"></a></h2>
<p>The only types currently in the “strided” category are
specializations of <code>strided_slice</code>. Future versions of C++
might expand this category. In our view, this would have little benefit.
Nevertheless, nothing stops WG21 from doing so. For example, C++29 might
(hypothetically) make a strided slice any type that meets the following
exposition-only <em><code>strided-slice&lt;index_type&gt;</code></em>
concept.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">concept</span> integral_not_bool <span class="op">=</span> std<span class="op">::</span>is_integral_v<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">!</span> std<span class="op">::</span>is_same_v<span class="op">&lt;</span>T, <span class="dt">bool</span><span class="op">&gt;</span>;</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> IndexType, <span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="kw">concept</span> <em>strided-slice</em> <span class="op">=</span> semiregular<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>  convertible_to<span class="op">&lt;</span>T<span class="op">::</span>offset_type, IndexType<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>  convertible_to<span class="op">&lt;</span>T<span class="op">::</span>extent_type, IndexType<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>  convertible_to<span class="op">&lt;</span>T<span class="op">::</span>stride_type, IndexType<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>  <span class="kw">requires</span><span class="op">(</span>T t<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> t<span class="op">.</span>offset <span class="op">}</span> <span class="op">-&gt;</span> convertible_to<span class="op">&lt;</span>IndexType<span class="op">&gt;</span>;</span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> t<span class="op">.</span>extent <span class="op">}</span> <span class="op">-&gt;</span> convertible_to<span class="op">&lt;</span>IndexType<span class="op">&gt;</span>;</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> t<span class="op">.</span>stride <span class="op">}</span> <span class="op">-&gt;</span> convertible_to<span class="op">&lt;</span>IndexType<span class="op">&gt;</span>;</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span></code></pre></div>
<p>The following user-defined type <code>my_slice</code> would meet
<em><code>strided-slice</code></em><code>&lt;size_t&gt;</code>, even
though</p>
<ul>
<li><p>it is not convertible to or from
<code>strided_slice&lt;size_t, size_t, size_t&gt;</code>;</p></li>
<li><p>it has a base class, which <code>strided_slice</code>
specializations cannot, per [mdspan.sub.strided.slice] 2; and</p></li>
<li><p>it has members other than <code>offset</code>,
<code>extent</code>, and <code>stride</code>, which
<code>strided_slice</code> cannot, per [mdspan.sub.strided.slice]
2.</p></li>
</ul>
<div class="sourceCode" id="cb3"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> my_base <span class="op">{}</span>;</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> my_slice <span class="op">:</span> <span class="kw">public</span> my_base <span class="op">{</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">using</span> offset_type <span class="op">=</span> <span class="dt">size_t</span>;</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">using</span> extent_type <span class="op">=</span> <span class="dt">size_t</span>;</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">using</span> stride_type <span class="op">=</span> <span class="dt">size_t</span>;</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>  </span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>  offset_type offset;</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>  extent_type extent;</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>  stride_type stride;</span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>  <span class="co">// User-declared constructor makes this not an aggregate</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>  my_slice<span class="op">(</span>extent_type ext<span class="op">)</span> <span class="op">:</span> offset<span class="op">(</span><span class="dv">0</span><span class="op">)</span>, extent<span class="op">(</span>ext<span class="op">)</span>, stride<span class="op">(</span><span class="dv">1</span><span class="op">)</span> <span class="op">{}</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>  <span class="co">// strided_slice is not allowed to have extra members</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a>  std<span class="op">::</span>string label <span class="op">=</span> <span class="st">&quot;my favorite slice label&quot;</span>;</span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a>  std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span>, <span class="dv">3</span><span class="op">&gt;</span> member_function<span class="op">()</span> <span class="kw">const</span> <span class="op">{</span> <span class="cf">return</span> <span class="op">{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span>; <span class="op">}</span></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a>  <span class="co">// my_slice is not convertible to or from strided_slice</span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> Offset, <span class="kw">class</span> Extent, <span class="kw">class</span> Stride<span class="op">&gt;</span></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a>  my_slice<span class="op">(</span>strided_slice<span class="op">&lt;</span>Offset, Extent, Stride<span class="op">&gt;)</span> <span class="op">=</span> <span class="kw">delete</span>;</span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> Offset, <span class="kw">class</span> Extent, <span class="kw">class</span> Stride<span class="op">&gt;</span></span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a>  <span class="kw">operator</span> strided_slice<span class="op">&lt;</span>Offset, Extent, Stride<span class="op">&gt;()</span> <span class="kw">const</span> <span class="op">=</span> <span class="kw">delete</span>;</span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
<p>Now suppose that a user has defined a <code>submdspan_mapping</code>
customization in a C++26 - compatible way for their custom layout
mapping <code>custom_layout::mapping</code>. Then, <code>my_slice</code>
likely would not work for this layout mapping in C++29, even though the
hypothetical <code>_strided-slice</code>_ concept makes it possible to
interpret <code>my_slice</code> unambiguously as a strided slice, and to
write generic code that does so.</p>
<p>Unlike with contiguous subrange slices, users have no way to
anticipate all possible ways that future Standard versions might expand
the set of valid strided slice types. For example, C++29 could decide
that strided slices must be aggregate types (which would exclude the
above <code>my_base</code>), or that they also include any
<em><code>tuple-like</code></em> type with 3 members all convertible to
<code>index_type</code> (not what we prefer, but WG21 has the freedom to
do this). Furthermore, <code>my_slice</code> with
<code>stride = cw&lt;1, size_t&gt;</code> should reasonably count as a
contiguous subrange slice type; expanding one set risks conflicts
between sets.</p>
<h2 data-number="4.6" id="wg21-may-add-a-new-slice-category"><span class="header-section-number">4.6</span> WG21 may add a new slice
category<a href="#wg21-may-add-a-new-slice-category" class="self-link"></a></h2>
<p>The same problem would arise if future versions of C++ introduce a
new category of slices. For example, suppose that C++29 hypothetically
adds an “indirect slice” category that takes an arbitrary tuple of
indices to include in that extent of the slice. The Mandates of
<code>submdspan</code> ([mdspan.sub.sub] 4.3) make it ill-formed to call
with a new kind of slice, so this hypothetical C++29 addition would need
to add a new clause there. That would not be a breaking change in
itself. The problem is that nothing currently requires
<code>submdspan_mapping</code> to be ill-formed when any of its slices
is not a valid slice type.</p>
<h1 data-number="5" id="possible-solution-make-submdspan-canonicalize-slices"><span class="header-section-number">5</span> Possible solution: Make
<code>submdspan</code> canonicalize slices<a href="#possible-solution-make-submdspan-canonicalize-slices" class="self-link"></a></h1>
<h2 data-number="5.1" id="summary-1"><span class="header-section-number">5.1</span> Summary<a href="#summary-1" class="self-link"></a></h2>
<p>There is a tension between <code>submdspan</code> users and custom
mapping authors. Users of <code>submdspan</code> want it to work with
all kinds of slice types. For example, they might want to write their
own tuple types that are standard-layout if all their template
parameters are (unlike <code>std::tuple</code>), and would expect
<code>submdspan</code> to work with them. In contrast, users who
customize <code>submdspan_mapping</code> for their own layout mappings
want a “future-proof” interface. They only want <code>submdspan</code>
to call <code>submdspan_mapping</code> for a known and unchanging set of
types.</p>
<p>One solution to this tension is to change <code>submdspan</code> so
that it “canonicalizes” its slice inputs before passing them to
<code>submdspan_mapping</code>. The <code>submdspan</code> function
still takes any valid slice types, but it maps each input slice type to
a “canonical” slice type according to rules that we will explain below.
As a result, a <code>submdspan_mapping</code> customization would ever
be called with the following concrete list of slice types.</p>
<ol type="1">
<li><p><code>index_type</code> (that is, the layout mapping’s
<code>index_type</code>)</p></li>
<li><p><code>constant_wrapper&lt;Value, index_type&gt;</code> for some
<code>Value</code></p></li>
<li><p><code>strided_slice</code> where each member is either
<code>index_type</code> or
<code>constant_wrapper&lt;Value, index_type&gt;</code> for some
<code>Value</code></p></li>
<li><p><code>full_extent_t</code></p></li>
</ol>
<p>The <code>constant_wrapper</code> class template comes from <a href="https://isocpp.org/files/papers/P2781R7.html">P2187R7</a>, which
LEWG design-approved (with design changes not relevant to this proposal)
and forwarded to LWG on 2025/03/11 for C++26.</p>
<p>This solution would make it always ill-formed to call an existing
<code>submdspan_mapping</code> customization with a new kind of slice.
With the status quo, calling an existing <code>submdspan_mapping</code>
customization with a new kind of slice would be ill-formed <em>at
best</em>, and could be a precondition violation at worst, because
nothing prevents <code>submdspan</code> from ever calling existing
<code>submdspan_mapping</code> customizations with new slice types.</p>
<h2 data-number="5.2" id="canonicalization-rules"><span class="header-section-number">5.2</span> Canonicalization rules<a href="#canonicalization-rules" class="self-link"></a></h2>
<p>Here are the canonicalization rules for an input slice <code>s</code>
of type <code>S</code> and a input mapping whose extents type has index
type <code>index_type</code>. The result depends only on the input
mapping’s extents type and the slices.</p>
<ol type="1">
<li><p>If <code>S</code> is convertible to <code>index_type</code>, then
<em><code>canonical-ice</code></em><code>&lt;index_type&gt;(s)</code>;</p></li>
<li><p>else, if <code>S</code> models
<em><code>index-pair-like</code></em><code>&lt;index_type&gt;</code>,
then
<code>strided_slice{.offset=</code><em><code>canonical-ice</code></em><code>&lt;index_type&gt;(get&lt;0&gt;(s)), .extent=</code><em><code>subtract-ice</code></em><code>&lt;index_type&gt;(get&lt;1&gt;(s), get&lt;0&gt;(s)), .stride=cw&lt;index_type(1)&gt;}</code>.</p></li>
<li><p>else, if <code>S</code> is convertible to
<code>full_extent_t</code>, then <code>full_extent_t</code>;</p></li>
<li><p>else, if <code>S</code> is a specialization of
<code>strided_slice</code>, then
<code>strided_slice{.offset=</code><em><code>canonical-ice</code></em><code>&lt;index_type&gt;(s.offset), .extent=</code><em><code>canonical-ice</code></em><code>&lt;index_type&gt;(s.extent), .stride=</code><em><code>canonical-ice</code></em><code>&lt;index_type&gt;(s.stride)}</code>.</p></li>
</ol>
<p>The two exposition-only functions <em><code>canonical-ice</code></em>
and <em><code>subtract-ice</code></em> preserve “compile-time-ness” of
any <em><code>integral-constant-like</code></em> arguments, and force
all inputs to either <code>index_type</code> or
<code>constant_wrapper&lt;Value, index_type&gt;</code> for some value
<code>Value</code>. The <code>cw</code> variable template comes from <a href="https://isocpp.org/files/papers/P2781R7.html">P2187R7</a>. Rule
(1) mimics the behavior of the exposition-only function
<em><code>first_</code></em> ([mdspan.sub.helpers] 1 - 4).</p>
<h2 data-number="5.3" id="how-to-apply-canonicalization"><span class="header-section-number">5.3</span> How to apply canonicalization<a href="#how-to-apply-canonicalization" class="self-link"></a></h2>
<p>The function <code>submdspan_canonicalize_slices</code> would
canonicalize all the slices at once. It takes as arguments the input
mapping’s extents and the input slices of
<code>submdspan_mapping</code>. Canonicalization would make
<code>submdspan</code> equivalent to the following code.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> <span class="op">[...</span>canonicalize_slices<span class="op">]</span> <span class="op">=</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>  submdspan_canonicalize_slices<span class="op">(</span>src<span class="op">.</span>extents<span class="op">()</span>, slices<span class="op">...)</span>;</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> sub_map_result <span class="op">=</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>  submdspan_mapping<span class="op">(</span>src<span class="op">.</span>mapping<span class="op">()</span>, canonicalize_slices<span class="op">...)</span>;</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="cf">return</span> mdspan<span class="op">(</span>src<span class="op">.</span>accessor<span class="op">().</span>offset<span class="op">(</span>src<span class="op">.</span>data<span class="op">()</span>, sub_map_result<span class="op">.</span>offset<span class="op">)</span>,</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>              sub_map_result<span class="op">.</span>mapping,</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>              AccessorPolicy<span class="op">::</span>offset_policy<span class="op">(</span>src<span class="op">.</span>accessor<span class="op">()))</span>;</span></code></pre></div>
<p>The <code>auto [...canonical_slices]</code> bit of code above uses
the “structured bindings can introduce a pack” feature introduced by <a href="https://wg21.link/p1061r10">P1061R10</a> into C++26. The
<code>submdspan_canonicalize_slices</code> function takes the extents
for two reasons.</p>
<ol type="1">
<li><p>A slice’s canonical type depends on the extents’
<code>index_type</code>.</p></li>
<li><p>Knowing the extents lets it enforce preconditions on the slices,
so that the resulting canonical slices don’t need to be checked again.
This is why canonicalization is not just a function to be called on each
slice without considering its corresponding input extent.</p></li>
</ol>
<h2 data-number="5.4" id="what-about-precondition-checking"><span class="header-section-number">5.4</span> What about precondition
checking?<a href="#what-about-precondition-checking" class="self-link"></a></h2>
<p>The <code>submdspan</code> function imposes preconditions on both its
slice arguments and on the result of <code>submdspan_mapping</code> (<a href="https://eel.is/c++draft/views.multidim#mdspan.sub.sub-5">[mdspan.sub.sub]
5</a>). The preconditions on <code>submdspan_mapping</code>’s result
require that the user’s customization give a mapping that views exactly
those elements of the input <code>mdspan</code> that are specified by
the slices. That is, the customization has to be correct, given valid
input slices. Enforcement of this is the user’s responsibility.</p>
<p>Enforcement of preconditions on the input slices is the
implementation’s responsibility. This depends only on the input slices
and the input mapping’s extents. Just as <code>mdspan</code> can check
bounds for the input of the <code>at</code> function using the input and
its extents, <code>submdspan</code> can enforce all preconditions on the
slices using the input mapping’s extents. The current wording expresses
these preconditions identically in two places:</p>
<ol type="1">
<li><p>on the slice arguments of <code>submdspan_extents</code> (<a href="https://eel.is/c++draft/views.multidim#mdspan.sub.extents-3">[mdspan.sub.extents]
3</a>), and</p></li>
<li><p>on the slice arguments of <code>submdspan</code> (<a href="https://eel.is/c++draft/views.multidim#mdspan.sub.sub-5">[mdspan.sub.sub]
5</a>).</p></li>
</ol>
<p>Passing the slices through <code>submdspan_extents</code> is not
enough to ensure that <code>submdspan_mapping</code>’s slice arguments
do not violate its (unstated) preconditions. This is because deciding
the result mapping’s type and constructing it may depend on the slices,
not just the result’s extents. Another way to say this is that different
<code>slices...</code> arguments might result in the same extents, but a
different layout mapping. For example, if <code>src</code> is a rank-1
<code>layout_left</code> mapping with extents
<code>dims&lt;1&gt;{10}</code>, both <code>full_extent</code> and
<code>strided_slice{.offset=0, .extent=src.extent(0), .stride=1}</code>
would result in extents <code>dims&lt;1&gt;{10}</code>, but different
layouts (<code>layout_left</code> and <code>layout_stride</code>,
respectively). This means that slice canonicalization is also an
opportunity to avoid duplication of precondition-checking code.</p>
<p>There are two kinds of preconditions on slices:</p>
<ol type="1">
<li><p>that all indices represented by the slice can be represented as
values of type <code>extents::index_type</code>, that is, that casting
them to <code>extents::index_type</code> does not cause integer
overflow; and</p></li>
<li><p>that all indices represented by slice <code>k</code> are in <span class="math inline">[0</span>, <code>src.extent(k)</code> <span class="math inline">)</span>.</p></li>
</ol>
<p>The current wording expresses both preconditions via the
exposition-only functions <em><code>first_</code></em> and
<em><code>last_</code></em>. The specification of each of these uses
<code>extents::</code><em><code>index-cast</code></em> to preprocess
their return values (see <a href="https://eel.is/c++draft/views.multidim#mdspan.sub.helpers-4">[mdspan.sub.helpers
4]</a> and <a href="https://eel.is/c++draft/views.multidim#mdspan.sub.helpers-9">[mdspan.sub.helpers
9]</a>). The <em><code>index-cast</code></em> exposition-only function
<a href="https://eel.is/c++draft/views.multidim#mdspan.extents.expo-9">[mdspan.extents.expo]
9</a> is a hook to enforce the “no overflow” precondition. The “in the
extent’s range” precondition is currently up to <code>submdspan</code>
implementations to check.</p>
<p>Slice canonicalization needs to have at least the “no overflow”
precondition, because any integer results are either
<code>index_type</code> or
<code>constant_wrapper&lt;Value, index_type&gt;</code> for some
<code>Value</code>. That is, it always casts input to
<code>index_type</code>. We propose applying the “in the extent’s range”
precondition to slice canonicalization as well. This would let
<code>submdspan</code> implementations put all their precondition
checking (if any) in the canonicalization function.</p>
<h2 data-number="5.5" id="what-about-performance"><span class="header-section-number">5.5</span> What about performance?<a href="#what-about-performance" class="self-link"></a></h2>
<p>We have not measured the performance effects of this approach.
Everything we write here is speculation.</p>
<p>In terms of compile-time performance, canonicalization would minimize
the set of instantiations of both <code>submdspan_mapping</code> and
<code>submdspan_extents</code> (which customizations of
<code>submdspan_mapping</code> may call).</p>
<p>In terms of run-time performance, customizations would likely need to
copy slices into their canonical forms. This would introduce new local
variables. Slices tend to be simple, and are made of combinations of
zero to three integers and empty types (like
<code>constant_wrapper</code> or <code>full_extent_t</code>). Thus, it
should be easy for compilers to optimize away all the extra types. On
the other hand, if they can’t, then the compiler may need to reserve
extra hardware resources (like registers and stack space) for the extra
local variables. This <em>may</em> affect performance, especially in
tight loops. Mitigations generally entail creating subview layout
mappings by hand.</p>
<p>The proposed set of canonicaliation rules would require turning every
<em><code>pair-like</code></em> type into a <code>strided_slice</code>.
Pair-like slices are common enough in practice that we may want to
optimize specially for them; see below.</p>
<h2 data-number="5.6" id="hypothetical-performance-mitigations"><span class="header-section-number">5.6</span> Hypothetical performance
mitigations<a href="#hypothetical-performance-mitigations" class="self-link"></a></h2>
<p>Here are some options that might mitigate some performance concerns.
All such concerns are hypothetical without actual performance
measurements.</p>
<ol type="1">
<li><p>Let Standard layout mappings accept arbitrary slice types.
Restrict the proposed changes to user-defined layout mappings. This
would optimize for the common case of Standard layout mappings. On the
other hand, performing canonicalization for all
<code>submdspan_mapping</code> customizations (including the Standard
ones) would simplify implementations, especially for checking
preconditions.</p></li>
<li><p>Expand the canonicalization rules so that <code>pair</code> and
<code>tuple</code> slices are passed through, instead of being
transformed into <code>strided_slice</code>. This would optimize a
common case, at the cost of making <code>submdspan_mapping</code>
customizations handle more slice types. That would complicate the code
and possibly increase compile-time cost, thus taking away at least some
of the benefits of canonicalization.</p></li>
</ol>
<h2 data-number="5.7" id="implementation"><span class="header-section-number">5.7</span> Implementation<a href="#implementation" class="self-link"></a></h2>
<p><a href="https://godbolt.org/z/TPWqdh15E">This Compiler Explorer
link</a> offers a brief and hasty implementation of this solution.</p>
<h2 data-number="5.8" id="wording-for-this-solution"><span class="header-section-number">5.8</span> Wording for this solution<a href="#wording-for-this-solution" class="self-link"></a></h2>
<blockquote>
<p>Text in blockquotes is not proposed wording, but rather instructions
for generating proposed wording.</p>
</blockquote>
<blockquote>
<p>Assume that <a href="https://isocpp.org/files/papers/P2781R7.html">P2187R7</a> plus
fixes from LEWG on its 2025/03/11 review have been applied to the
Working Draft. (EDITORIAL NOTE: This makes <code>constant_wrapper</code>
and <code>cw</code> available to the rest of the Standard Library.)</p>
</blockquote>
<h3 data-number="5.8.1" id="increment-__cpp_lib_submdspan-feature-test-macro"><span class="header-section-number">5.8.1</span> Increment
<code>__cpp_lib_submdspan</code> feature test macro<a href="#increment-__cpp_lib_submdspan-feature-test-macro" class="self-link"></a></h3>
<p>In [version.syn], increase the value of the
<code>__cpp_lib_submdspan</code> macro by replacing YYYMML below with
the integer literal encoding the appropriate year (YYYY) and month
(MM).</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#define __cpp_lib_submdspan </span>YYYYMML<span class="pp"> </span><span class="co">// also in &lt;mdspan&gt;</span></span></code></pre></div>
<h3 data-number="5.8.2" id="change-mdspan.sub.helpers"><span class="header-section-number">5.8.2</span> Change [mdspan.sub.helpers]<a href="#change-mdspan.sub.helpers" class="self-link"></a></h3>
<blockquote>
<p>Change [mdspan.sub.helpers] as follows.</p>
</blockquote>
<div class="sourceCode" id="cb6"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">constexpr</span> T <em>de-ice</em><span class="op">(</span>T val<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> val; <span class="op">}</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span>integral<span class="op">-</span>constant<span class="op">-</span>like T<span class="op">&gt;</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">constexpr</span> <span class="kw">auto</span> <em>de-ice</em><span class="op">(</span>T<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> T<span class="op">::</span>value; <span class="op">}</span></span></code></pre></div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb7"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>template&lt;class IndexType, <em>integral-constant-like</em> S&gt;</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>constexpr constant_wrapper&lt;S::value, IndexType&gt; <em>canonical-ice</em>(S s) {</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>  return {};</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>template&lt;class IndexType, class S&gt;</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>constexpr IndexType <em>canonical-ice</em>(S s) {</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>  return s;</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a>template&lt;class IndexType, class X, class Y&gt;</span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a>constepxr auto <em>subtract-ice</em>(X x, Y y) {</span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a>  if constexpr (<em>integral-constant-like</em>&lt;X&gt; &amp;&amp; <em>integral-constant-like</em>&lt;Y&gt;) {</span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a>    return cw&lt;IndexType(<em>de-ice</em>(y) - <em>de-ice</em>(x))&gt;;</span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a>  else {</span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>    return IndexType(y - x);</span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>

</div>
<div class="sourceCode" id="cb8"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> IndexType, <span class="dt">size_t</span> k, <span class="kw">class</span><span class="op">...</span> SliceSpecifiers<span class="op">&gt;</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">constexpr</span> IndexType first_<span class="op">(</span>SliceSpecifiers<span class="op">...</span> slices<span class="op">)</span>;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<em>Mandates</em>: <code>IndexType</code> is a signed or unsigned
integer type.</p>
<p>[<em>Editorial note</em>: Skip down to the end of the section –
<em>end note</em>]</p>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb9"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>template&lt;class IndexType, size_t... Extents, class... Slices&gt;</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>constexpr auto submdspan_canonicalize_slices(</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>  const extents&lt;IndexType, Extents...&gt;&amp; src, Slices... slices);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">12</a></span>
<em>Constraints</em>: <code>sizeof...(slices)</code> equals
<code>sizeof...(Extents)</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">13</a></span>
<em>Mandates</em>: For each rank index <span class="math inline"><em>k</em></span> of <code>src</code>, exactly one
of the following is <code>true</code>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(13.1)</a></span>
<span class="math inline"><em>S</em><sub><em>k</em></sub></span> models
<code>convertible_to&lt;IndexType&gt;</code>,</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(13.2)</a></span>
<span class="math inline"><em>S</em><sub><em>k</em></sub></span> models
<em><code>index-pair-like&lt;IndexType&gt;</code></em>,</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(13.3)</a></span>
<code>is_convertible_v&lt;</code> <span class="math inline"><em>S</em><sub><em>k</em></sub></span>
<code>, full_extent_t&gt;</code> is <code>true</code>, or</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(13.4)</a></span>
<span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of <code>strided_slice</code>.</p></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">14</a></span>
<em>Preconditions</em>: For each rank index <span class="math inline"><em>k</em></span> of <code>src</code>, all of the
following are <code>true</code>.</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.1)</a></span> if
<span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of <code>strided_slice</code></p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.1.1)</a></span>
<span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.extent</code>
= 0, or</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.1.2)</a></span>
<span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.stride</code>
&gt; 0</p></li>
</ul></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.2)</a></span> 0 ≤
<em><code>first_</code></em><code>&lt;IndexType,</code><span class="math inline"><em>k</em></span><code>&gt;(slices...)</code> ≤
<em><code>last_</code></em><code>&lt;</code><span class="math inline"><em>k</em></span><code>&gt;(src, slices...)</code> ≤
<code>src.extent(</code><span class="math inline"><em>k</em></span><code>)</code></p></li>
</ul>
<p>[<em>Editorial note</em>: These are the same as the preconditions on
the slice inputs of <code>submdspan_extents</code> and
<code>submdspan</code>. They must be imposed here, before conversion to
<code>IndexType</code> or
<code>constant_wrapper&lt;IndexType, Value&gt;</code>, in order to
include the precondition that forbids integer overflow. – <em>end
note</em>]</p>
<p><span class="marginalizedparent"><a class="marginalized">15</a></span>
<em>Returns</em>: a <code>tuple</code> of <code>src.rank()</code>
elements, where for each rank index <span class="math inline"><em>k</em></span> of <code>src</code>, the following
specifies element <span class="math inline"><em>k</em></span> of the
return value:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(15.1)</a></span> If
<span class="math inline"><em>S</em><sub><em>k</em></sub></span> models
<code>convertible_to&lt;IndexType&gt;</code>, then
<em><code>canonical-ice</code></em><code>&lt;IndexType&gt;(s)</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(15.2)</a></span>
else, if <span class="math inline"><em>S</em><sub><em>k</em></sub></span> models
<em><code>index-pair-like</code></em><code>&lt;IndexType&gt;</code>,
then
<code>strided_slice{.offset=</code><em><code>canonical-ice</code></em><code>&lt;IndexType&gt;(get&lt;0&gt;(s)), .extent=</code><em><code>subtract-ice</code></em><code>&lt;IndexType&gt;(get&lt;1&gt;(s), get&lt;0&gt;(s)), .stride=cw&lt;IndexType(1)&gt;}</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(15.3)</a></span>
else, if <code>is_convertible_v&lt;</code> <span class="math inline"><em>S</em><sub><em>k</em></sub></span>
<code>, full_extent_t&gt;</code> is <code>true</code>, then
<code>full_extent_t</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(15.4)</a></span>
else, if <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of <code>strided_slice</code>, then
<code>strided_slice{.offset=</code><em><code>canonical-ice</code></em><code>&lt;IndexType&gt;(s.offset), .extent=</code><em><code>canonical-ice</code></em><code>&lt;IndexType&gt;(s.extent), .stride=</code><em><code>canonical-ice</code></em><code>&lt;IndexType&gt;(s.stride)}</code>.</p></li>
</ul>

</div>
<h3 data-number="5.8.3" id="requirements-of-all-submdspan_mapping-customizations"><span class="header-section-number">5.8.3</span> Requirements of all
<code>submdspan_mapping</code> customizations<a href="#requirements-of-all-submdspan_mapping-customizations" class="self-link"></a></h3>
<blockquote>
<p>Change [mdspan.sub.map.common] as follows.</p>
</blockquote>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
The following elements apply to all functions <span class="rm" style="color: #bf0303"><del>in [mdspan.sub.map]</del></span><span class="add" style="color: #00AA00"><ins>named
<span><code>submdspan_mapping</code></span> that would be found by
overload resolution when called with a layout mapping
<span><code>m</code></span> as their first argument and a parameter pack
<span><code>slices</code></span> as their remaining
arguments(s)</ins></span>.</p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
<em>Constraints</em>: <code>sizeof...(slices)</code> equals
<code>extents_type::rank()</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
<em>Mandates</em>: For each rank index <span class="math inline"><em>k</em></span> of <code>extents()</code>, exactly
one of the following is <code>true</code>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.1)</a></span>
<span class="math inline"><em>S</em><sub><em>k</em></sub></span> <span class="rm" style="color: #bf0303"><del>models
<span><code>convertible_to&lt;index_type&gt;</code></span></del></span><span class="add" style="color: #00AA00"><ins>is
<span><code>index_type</code></span></ins></span>,</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.2)</a></span>
<span class="math inline"><em>S</em><sub><em>k</em></sub></span> <span class="rm" style="color: #bf0303"><del>models
<em><span><code>index-pair-like</code></span></em><span><code>&lt;index_type&gt;</code></span></del></span><span class="add" style="color: #00AA00"><ins>is
<span><code>constant_wrapper&lt;Value, index_type&gt;</code></span> for
some value <span><code>Value</code></span> of type
<span><code>index_type</code></span> such that 0 ≤
<span><code>Value</code></span> &lt;
<span><code>extents().extent(0)</code></span></ins></span>,</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.3)</a></span>
<span class="rm" style="color: #bf0303"><del><span><code>is_convertible_v&lt;</code></span>
<span class="math inline"><em>S</em><sub><em>k</em></sub></span>
<span><code>, full_extent_t&gt;</code></span> is
<span><code>true</code></span></del></span><span class="add" style="color: #00AA00"><ins><span class="math inline"><em>S</em><sub><em>k</em></sub></span> is
<span><code>full_extent_t</code></span></ins></span>, or</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.4)</a></span>
<span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of <code>strided_slice</code> <span class="add" style="color: #00AA00"><ins>where each member is either
<span><code>index_type</code></span> or
<span><code>constant_wrapper&lt;Value, index_type&gt;</code></span> for
some value <span><code>Value</code></span> of type
<span><code>extents_type::index_type</code></span> such that 0 ≤
<span><code>Value</code></span> &lt;
<span><code>extents().extent(0)</code></span></ins></span>.</p></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Preconditions</em>: For each rank index <code>k</code> of
<code>extents()</code>, all of the following are <code>true</code>:</p>
<h3 data-number="5.8.4" id="submdspan-function-template"><span class="header-section-number">5.8.4</span> <code>submdspan</code>
function template<a href="#submdspan-function-template" class="self-link"></a></h3>
<blockquote>
<p>Change [mdspan.sub.sub] (“<code>submdspan</code> function template”)
as follows.</p>
</blockquote>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
Let <code>index_type</code> be
<code>typename Extents::index_type</code>.</p>
<div class="add" style="color: #00AA00">
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
Let <code>canonical_slices</code> be the pack resulting from the
following statement.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>auto [...canonical_slices] = submdspan_canonicalize_slices(src.extents(), slices...).</span></code></pre></div>

</div>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
Let <code>sub_map_offset</code> be the result of
<code>submdspan_mapping(src.mapping(),</code> <span class="rm" style="color: #bf0303"><del><span><code>slices</code></span></del></span><span class="add" style="color: #00AA00"><ins><span><code>canonical_slices</code></span></ins></span>
<code>...)</code>.</p>
<p>[<em>Note 1</em>: This invocation of <code>submdspan_mapping</code>
selects a function call via overload resolution on a candidate set that
includes the lookup set found by argument-dependent lookup
([basic.lookup.argdep]). — <em>end note</em>]</p>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Constraints</em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span>
<code>sizeof...(slices)</code> equals <code>Extents​::​rank()</code>,
and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span> the
expression <code>submdspan_mapping(src.mapping(),</code> <span class="rm" style="color: #bf0303"><del><span><code>slices</code></span></del></span><span class="add" style="color: #00AA00"><ins><span><code>canonical_slices</code></span></ins></span>
<code>...)</code> is well-formed when treated as an unevaluated
operand.</p></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
<em>Mandates</em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.1)</a></span>
<code>decltype(submdspan_mapping(src.mapping(),</code> <span class="rm" style="color: #bf0303"><del><span><code>slices</code></span></del></span><span class="add" style="color: #00AA00"><ins><span><code>canonical_slices</code></span></ins></span>
<code>...))</code> is a specialization of
<code>submdspan_mapping_result</code>.</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.2)</a></span>
<code>is_same_v&lt;remove_cvref_t&lt;decltype(sub_map_offset.mapping.extents())&gt;, decltype(submdspan_extents(src.mapping(),</code>
<span class="rm" style="color: #bf0303"><del><span><code>slices</code></span></del></span><span class="add" style="color: #00AA00"><ins><span><code>canonical_slices</code></span></ins></span>
<code>...))&gt;</code> is <code>true</code>.</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.3)</a></span> For
each rank index <code>k</code> of <code>src.extents()</code>, exactly
one of the following is <code>true</code>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.3.1)</a></span>
<span class="math inline"><em>S</em><sub><em>k</em></sub></span> models
<code>convertible_to&lt;index_type&gt;</code>,</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.3.2)</a></span>
<span class="math inline"><em>S</em><sub><em>k</em></sub></span> models
<em><code>index-pair-like</code></em><code>&lt;index_type&gt;</code>,</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.3.3)</a></span>
<code>is_convertible_v&lt;</code> <span class="math inline"><em>S</em><sub><em>k</em></sub></span>
<code>, full_extent_t&gt;</code> is <code>true</code>, or</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.3.4)</a></span>
<span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of <code>strided_slice</code>.</p></li>
</ul></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">6</a></span>
<em>Preconditions</em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.1)</a></span> For
each rank index <span class="math inline"><em>k</em></span> of
<code>src.extents()</code>, all of the following are
<code>true</code>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.1.1)</a></span> if
<span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of <code>strided_slice</code></p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.1.1.1)</a></span>
<span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.extent</code>
= 0, or</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.1.1.2)</a></span>
<span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.stride</code>
&gt; 0</p></li>
</ul></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.1.2)</a></span> 0
≤ <em><code>first_</code></em><code>&lt;index_type,</code><span class="math inline"><em>k</em></span><code>&gt;(</code> <span class="rm" style="color: #bf0303"><del><span><code>slices</code></span></del></span><span class="add" style="color: #00AA00"><ins><span><code>canonical_slices</code></span></ins></span>
<code>)</code> ≤ <em><code>last_</code></em><code>&lt;</code><span class="math inline"><em>k</em></span><code>&gt;(src.extents(),</code>
<span class="rm" style="color: #bf0303"><del><span><code>slices</code></span></del></span><span class="add" style="color: #00AA00"><ins><span><code>canonical_slices</code></span></ins></span>
<code>...)</code> ≤ <code>src.extent(k)</code></p></li>
</ul></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.2)</a></span>
<code>sub_map_offset.mapping.extents() == submdspan_extents(src.mapping(),</code>
<span class="rm" style="color: #bf0303"><del><span><code>slices</code></span></del></span><span class="add" style="color: #00AA00"><ins><span><code>canonical_slices</code></span></ins></span>
<code>...)</code> is <code>true</code>; and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.3)</a></span> for
each integer pack <code>I</code> which is a multidimensional index in
<code>sub_map_offset.mapping.extents()</code>,</p></li>
</ul>
<div class="rm" style="color: #bf0303">

<div class="sourceCode" id="cb11"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>sub_map_offset.mapping(I...) + sub_map_offset.offset ==</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>  src.mapping()(<em>src-indices</em>(array{I...}, slices...))</span></code></pre></div>

</div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb12"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>sub_map_offset.mapping(I...) + sub_map_offset.offset ==</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>  src.mapping()(<em>src-indices</em>(array{I...}, canonical_slices...))</span></code></pre></div>

</div>
<p>is <code>true</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">7</a></span>
<em>Effects</em>: Equivalent to:</p>
<div class="rm" style="color: #bf0303">

<div class="sourceCode" id="cb13"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>auto sub_map_result = submdspan_mapping(src.mapping(), slices...);</span></code></pre></div>

</div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb14"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a>auto [...canonicalize_slices] =</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>  submdspan_canonicalize_slices(src.extents(), slices...);</span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>auto sub_map_result =</span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>  submdspan_mapping(src.mapping(), canonicalize_slices...);</span></code></pre></div>

</div>
<div class="sourceCode" id="cb15"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="cf">return</span> mdspan<span class="op">(</span>src<span class="op">.</span>accessor<span class="op">().</span>offset<span class="op">(</span>src<span class="op">.</span>data<span class="op">()</span>, sub_map_result<span class="op">.</span>offset<span class="op">)</span>,</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>              sub_map_result<span class="op">.</span>mapping,</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>              AccessorPolicy<span class="op">::</span>offset_policy<span class="op">(</span>src<span class="op">.</span>accessor<span class="op">()))</span>;</span></code></pre></div>
<h2 data-number="5.9" id="why-we-do-not-propose-this-solution"><span class="header-section-number">5.9</span> Why we do not propose this
solution<a href="#why-we-do-not-propose-this-solution" class="self-link"></a></h2>
<p>We think that canonicalization is the expected implementation
approach anyway, so there is no need to standardize it. Suppose that
P2769 is adopted into some future C++ version. Implementations might
thus expose users to an intermediate period in which they implement
C++26 but not C++29. During this period, users might write
<code>submdspan_mapping</code> customizations to the narrower definition
of <em><code>pair-like</code></em>. For example, users might write
<code>std::get</code> explicitly instead of using ADL-findable
<code>get</code>, which would only work with Standard Library types that
have overloaded <code>std::get</code>. This would, in turn, encourage
implementers of <code>submdspan</code> to “canonicalize”
<em><code>pair-like</code></em> slice arguments (e.g., into
<code>pair</code> or <code>tuple</code>) before calling
<code>submdspan_mapping</code> customizations, as that would maximize
backwards compatibility.</p>
<p>In summary, the possibility of later generalization of
<em><code>pair-like</code></em> would naturally lead high-quality
implementations to the desired outcome. That would minimize code changes
both for users, and for implementations (as the Standard Library
currently has five <code>submdspan_mapping</code> customizations of its
own). Forcing canonicalization for C++26 would merely anticipate the
expected implementation approach.</p>
<h1 data-number="6" id="other-solutions"><span class="header-section-number">6</span> Other solutions<a href="#other-solutions" class="self-link"></a></h1>
<h2 data-number="6.1" id="make-p2769s-submdspan-changes-a-dr-for-c26"><span class="header-section-number">6.1</span> Make P2769’s
<code>submdspan</code> changes a DR for C++26<a href="#make-p2769s-submdspan-changes-a-dr-for-c26" class="self-link"></a></h2>
<p>If P2769 is on track to be adopted into some future C++ version, then
we could lobby WG21 to treat P2769’s proposed changes to
<code>submdspan</code>’s wording as a Defect Report (DR) for C++26. This
would require no effort prior to acceptance of C++26. However,
implementations might expose users to an intermediate period in which
they implement C++26 but not the DR. This would have the same practical
effect on users as if we had applied the change to C++29 but not as a
DR.</p>
<h1 data-number="7" id="acknowledgements"><span class="header-section-number">7</span> Acknowledgements<a href="#acknowledgements" class="self-link"></a></h1>
<p>Thanks to Tomasz Kamiński for pointing out this issue.</p>
</div>
</div>
</body>
</html>
