<!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-05-16" />
  <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>P3663R1</td>
  </tr>
  <tr>
    <td>Date: </td>
    <td>2025-05-16</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="#solution-make-submdspan-canonicalize-slices" id="toc-solution-make-submdspan-canonicalize-slices"><span class="toc-section-number">5</span> 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="#canonicalization-is-reusable" id="toc-canonicalization-is-reusable"><span class="toc-section-number">5.4</span> Canonicalization is
reusable</a></li>
<li><a href="#what-about-precondition-checking" id="toc-what-about-precondition-checking"><span class="toc-section-number">5.5</span> What about precondition
checking?</a></li>
<li><a href="#why-implementers-should-do-this-anyway" id="toc-why-implementers-should-do-this-anyway"><span class="toc-section-number">5.6</span> Why implementers should do this
anyway</a></li>
</ul></li>
<li><a href="#performance" id="toc-performance"><span class="toc-section-number">6</span> Performance</a>
<ul>
<li><a href="#speculation-on-compile-time-and-run-time-effects" id="toc-speculation-on-compile-time-and-run-time-effects"><span class="toc-section-number">6.1</span> Speculation on compile-time and
run-time effects</a></li>
<li><a href="#hypothetical-performance-mitigations" id="toc-hypothetical-performance-mitigations"><span class="toc-section-number">6.2</span> Hypothetical performance
mitigations</a></li>
<li><a href="#submdspan-can-skip-canonicalization-for-standard-layout-mappings" id="toc-submdspan-can-skip-canonicalization-for-standard-layout-mappings"><span class="toc-section-number">6.3</span> <code>submdspan</code> can skip
canonicalization for Standard layout mappings</a></li>
</ul></li>
<li><a href="#implementation" id="toc-implementation"><span class="toc-section-number">7</span> Implementation</a></li>
<li><a href="#acknowledgements" id="toc-acknowledgements"><span class="toc-section-number">8</span> Acknowledgements</a></li>
<li><a href="#proposed-wording" id="toc-proposed-wording"><span class="toc-section-number">9</span> Proposed wording</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">9.1</span> Increment
<code>__cpp_lib_submdspan</code> feature test macro</a></li>
<li><a href="#change-mdspan.syn" id="toc-change-mdspan.syn"><span class="toc-section-number">9.2</span> Change [mdspan.syn]</a></li>
<li><a href="#change-mdspan.sub.helpers" id="toc-change-mdspan.sub.helpers"><span class="toc-section-number">9.3</span> Change
[mdspan.sub.helpers]</a></li>
<li><a href="#add-section-mdspan.sub.slices-slices-definitions-and-canonicalization" id="toc-add-section-mdspan.sub.slices-slices-definitions-and-canonicalization"><span class="toc-section-number">9.4</span> Add section [mdspan.sub.slices],
“Slices definitions and canonicalization”</a></li>
<li><a href="#change-section-mdspan.sub.extents" id="toc-change-section-mdspan.sub.extents"><span class="toc-section-number">9.5</span> Change section
[mdspan.sub.extents]</a></li>
<li><a href="#requirements-of-all-submdspan_mapping-customizations" id="toc-requirements-of-all-submdspan_mapping-customizations"><span class="toc-section-number">9.6</span> Requirements of all
<code>submdspan_mapping</code> customizations</a></li>
<li><a href="#change-mdspan.sub.map.common" id="toc-change-mdspan.sub.map.common"><span class="toc-section-number">9.7</span> Change
[mdspan.sub.map.common]</a></li>
<li><a href="#submdspan-function-template" id="toc-submdspan-function-template"><span class="toc-section-number">9.8</span> <code>submdspan</code> function
template</a></li>
</ul></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><p>Revision 0 to be submitted 2025-03-17</p>
<ul>
<li>2025-04-15: Reviewed by LEWG and forwarded to LWG.</li>
</ul></li>
<li><p>Revision 1 to be submitted 2025-05-19 for the pre-Sofia
mailing</p>
<ul>
<li><p>Add Mandates and Preconditions to
<em><code>canonical-ice</code></em> that the input be representable as a
value of type <code>IndexType</code>.</p></li>
<li><p>Fix <em><code>subtract-ice</code></em> to use
<code>constant_wrapper</code> arithmetic: that is, apply
<em><code>canonical-ice</code></em> first, then subtract the resulting
<code>constant_wrapper</code> objects.</p></li>
<li><p>Use structured binding for pair slices, not <code>get</code>;
reorder Mandates and Returns clauses to put the structured binding part
last. (For Mandates, this means that implementations need only try the
structured binding; if it fails, it’s ill-formed.)</p></li>
<li><p><code>submdspan</code> can’t actually be constrained on the
specific <code>submdspan_mapping(input.mapping(), slices...)</code> call
being well-formed, because there’s no way to constrain on a structured
binding declaration being well formed. (Structured binding is a
declaration, while constraints require expressions.) Instead, change the
constraint on <code>submdspan</code> to be that
<code>submdspan_mapping(input.mapping(), full_extent, ..., full_extent)</code>
is well-formed (call that an exposition-only concept
<em><code>mapping-sliceable-with-full-extents</code></em>), and make it
just a Mandate that
<code>submdspan_mapping(input.mapping(), slices...)</code> be
well-formed.</p></li>
<li><p>Add a new section for requirements on sliceable mappings, with a
new named requirement “sliceable layout mapping,” and an exception for
the standard mappings. The requirements are currently expressed in parts
of [mdspan.sub.map.common] and [mdspan.sub.sub] (e.g., 5.2 and 5.3, with
Note 2 after 5.3).</p></li>
<li><p>Add new function <code>submdspan_canonicalize_slices</code> to
<code>&lt;mdspan&gt;</code> header synopsis.</p></li>
<li><p>Fix non-wording Abstract and the rest of the non-wording sections
to say that we do actually propose these changes.</p></li>
<li><p>Add “Canonicalization is reusable” non-wording section to explain
why <code>submdspan_canonicalize_mapping</code> is not
exposition-only.</p></li>
<li><p>Update P2781 (<code>std::constant_wrapper</code>) references from
R7 to R8.</p></li>
<li><p>Add an Editorial Note to explain that the standard layout
mappings could accept all valid slice types, instead of just the
canonical ones. This should alleviate concerns about potential
performance effects of canonicalization. However, retain (in Editorial
Notes) suggested wording for the pass-through, just in case.</p></li>
<li><p>Define an exposition-only function
<em><code>check-static-bounds</code></em> to avoid repetition when
stating Mandates on canonical and valid slice types.</p></li>
</ul></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>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>We propose fixing this with a “canonicalization” approach. First,
define a fixed set of “canonical” slice types that represent all valid
slices, and a function <code>submdspan_canonicalize_slices</code> 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>
<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>“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>
<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>
</ol>
<p>The current Working Draft uses the term <em>unit-stride slice</em>
(defined in <a href="https://wg21.link/p3355">P3355</a>) 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>. The <em><code>pair-like</code></em> concept
depends in turn on the exposition-only concept
<em><code>tuple-like</code></em>. <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>It would be reasonable for <code>submdspan</code> to generalize
<em><code>pair-like</code></em> into any type for which structured
binding into two elements is well-formed. If the two elements are both
convertible to <code>index_type</code>, then this has an unambiguous
interpretation as a contiguous subrange. Thus, users should write their
customizations generically to that interface. However, in terms of the
Standard, there are two 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>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 any type for which a structured
binding into three elements is well-formed is a valid strided slice.
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="solution-make-submdspan-canonicalize-slices"><span class="header-section-number">5</span> Solution: Make
<code>submdspan</code> canonicalize slices<a href="#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>We propose resolving this tension by changing <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>full_extent_t</code>;</p></li>
<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>; or</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>
</ol>
<p>The <code>constant_wrapper</code> class template comes from <a href="https://wg21.link/p2781">P2781</a>. LEWG design-approved P2781R7
(with design changes not relevant to this proposal, which are expressed
as P2781R8) and forwarded it to LWG on 2025/03/11 for C++26. The only
aspect of a type that can vary in the above list is
<code>constant_wrapper</code>’s <code>Value</code>.</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>full_extent_t</code>,
then <code>full_extent_t</code>;</p></li>
<li><p>else, 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> 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>
<li><p>else, if <code>s</code> is destructurable into
<code>[first, last]</code> that are both convertible to
<code>index_type</code>, then
<code>strided_slice{.offset=</code><em><code>canonical-ice</code></em><code>&lt;index_type&gt;(first), .extent=</code><em><code>subtract-ice</code></em><code>&lt;index_type&gt;(last, first), .stride=cw&lt;index_type(1)&gt;}</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
<code>Value</code>. The <em><code>canonical-ice</code></em> function
also has Mandates or Preconditions (depending on whether its argument is
<em><code>integral-constant-like</code></em>) that its argument is
representable as a value of type <code>index_type</code>. This lets
implementations check for overflow. The <code>cw</code> variable
template comes from <a href="https://wg21.link/p2781">P2781</a>.</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> canonicalizes
all the slices at once. It takes as arguments the input mapping’s
extents and the input slices of <code>submdspan_mapping</code>.
Canonicalization makes <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 statement starting with <code>auto [...canonical_slices]</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.</p></li>
</ol>
<h2 data-number="5.4" id="canonicalization-is-reusable"><span class="header-section-number">5.4</span> Canonicalization is reusable<a href="#canonicalization-is-reusable" class="self-link"></a></h2>
<p>We expose <code>submdspan_canonicalize_slices</code>, instead of
making it exposition-only, because users may want to use it to build
their own functionality analogous to <code>submdspan</code>. For
example, consider a C++ type <code>small_matrix&lt;T, M, N&gt;</code>
representing a dense rank-2 array with element type <code>T</code> and
compile-time extents <code>M</code> and <code>N</code>. (This could be a
library exposure of a compiler extension, such as
<a href="https://clang.llvm.org/docs/MatrixTypes.html">Clang’s matrix
type extension</a>.) This type would expose access to individual
elements, but would <em>not</em> expose subviews. That is, getting an
<code>mdspan</code> that views any subset of elements would be
forbidden. However, <code>small_matrix</code> could expose
<em>copies</em> of subsets of elements, via a function
<code>submatrix</code> that takes the same slice specifiers as
<code>submdspan</code>.</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>small_matrix<span class="op">&lt;</span><span class="dt">float</span>, <span class="dv">5</span>, <span class="dv">7</span><span class="op">&gt;</span> A <span class="op">=</span> <span class="co">/* ... */</span>;</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> A_sub <span class="op">=</span> submatrix<span class="op">(</span>A,</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>  strided_slice<span class="op">{.</span>offset<span class="op">=</span><span class="dv">1</span>, <span class="op">.</span>extent<span class="op">=</span><span class="dv">4</span>, <span class="op">.</span>stride<span class="op">=</span><span class="dv">3</span><span class="op">}</span>,  <span class="co">// 1, 4</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>  strided_slice<span class="op">{.</span>offset<span class="op">=</span><span class="dv">0</span>, <span class="op">.</span>extent<span class="op">=</span><span class="dv">7</span>, <span class="op">.</span>stride<span class="op">=</span><span class="dv">2</span><span class="op">})</span>; <span class="co">// 0, 2, 4, 6 </span></span></code></pre></div>
<p>If <code>A</code> were a <code>layout_right</code>
<code>mdspan</code>, then result of <code>submdspan</code> with those
slices would be <code>layout_stride</code>. However, <code>A</code> is
not an <code>mdspan</code>. Its layout might be hidden from users. Even
if <code>small_matrix</code> layouts are known, there’s no need for
<code>submatrix</code> to return layouts in the same way as
<code>submdspan</code> does. For example, it might “pack” the result
into a contiguous <code>layout_right</code> <code>small_matrix</code>.
Nevertheless, the slice specifiers have the same interpretation for
<code>submatrix</code> as for <code>submdspan</code>; they are
independent of the input’s and result’ layout mappings. Thus, users who
want to implement <code>submatrix</code> could use
<code>submdspan_canonicalize_slices</code> to canonicalize
<code>submatrix</code>’s slice arguments.</p>
<p>This example explains why <code>submdspan_mapping</code> has a name
with the word “<code>submdspan</code>” in it, even though it is purely a
function of layout mappings and need not be tied to <code>mdspan</code>.
Calling <code>submdspan_mapping(mapping, slices...)</code> returns the
mapping that <code>submdspan(input, slices...)</code> would have for
<code>mapping = input.mapping()</code>.</p>
<h2 data-number="5.5" id="what-about-precondition-checking"><span class="header-section-number">5.5</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. Users who implement
a <code>submdspan_mapping</code> customization could thus assume that
the input slices are all correct.</p>
<h2 data-number="5.6" id="why-implementers-should-do-this-anyway"><span class="header-section-number">5.6</span> Why implementers should do this
anyway<a href="#why-implementers-should-do-this-anyway" class="self-link"></a></h2>
<p>Implementers should canonicalize slices anyway, even if this proposal
is not adopted. Suppose, for example, that P2769 (expanding the
definition of <em><code>tuple-like</code></em>) 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, which would only work with Standard
Library types that have overloaded <code>std::get</code>, instead of
using structured binding to get the two elements. 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). This proposal merely anticipates the expected implementation
approach.</p>
<h1 data-number="6" id="performance"><span class="header-section-number">6</span> Performance<a href="#performance" class="self-link"></a></h1>
<h2 data-number="6.1" id="speculation-on-compile-time-and-run-time-effects"><span class="header-section-number">6.1</span> Speculation on compile-time and
run-time effects<a href="#speculation-on-compile-time-and-run-time-effects" 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="6.2" id="hypothetical-performance-mitigations"><span class="header-section-number">6.2</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="6.3" id="submdspan-can-skip-canonicalization-for-standard-layout-mappings"><span class="header-section-number">6.3</span> <code>submdspan</code> can skip
canonicalization for Standard layout mappings<a href="#submdspan-can-skip-canonicalization-for-standard-layout-mappings" class="self-link"></a></h2>
<p>We believe that by the as-if rule, <code>submdspan</code> should be
able to skip canonicalization for layout mappings provided by the
Standard, even though we specify <code>submdspan</code> as “Effects:
Equivalent to” code that always canonicalizes slices. If LWG disagrees,
we offer alternate wording that enforces this permission.</p>
<h1 data-number="7" id="implementation"><span class="header-section-number">7</span> Implementation<a href="#implementation" class="self-link"></a></h1>
<p><a href="https://godbolt.org/z/TPWqdh15E">This Compiler Explorer
link</a> offers a brief and hasty implementation of this solution.</p>
<h1 data-number="8" id="acknowledgements"><span class="header-section-number">8</span> Acknowledgements<a href="#acknowledgements" class="self-link"></a></h1>
<p>Thanks to Tomasz Kamiński for pointing out this issue and for
suggesting improvements to the proposed wording.</p>
<h1 data-number="9" id="proposed-wording"><span class="header-section-number">9</span> Proposed wording<a href="#proposed-wording" class="self-link"></a></h1>
<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://wg21.link/p2781">P2781R8</a> has been
applied to the Working Draft. [<em>Editorial note</em>: This makes
<code>constant_wrapper</code> and <code>cw</code> available to the rest
of the Standard Library. – <em>end note</em>]</p>
</blockquote>
<h2 data-number="9.1" id="increment-__cpp_lib_submdspan-feature-test-macro"><span class="header-section-number">9.1</span> Increment
<code>__cpp_lib_submdspan</code> feature test macro<a href="#increment-__cpp_lib_submdspan-feature-test-macro" class="self-link"></a></h2>
<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="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="pp">#define __cpp_lib_submdspan </span>YYYYMML<span class="pp"> </span><span class="co">// also in &lt;mdspan&gt;</span></span></code></pre></div>
<p>[<em>Editorial note</em>: If this proposal is adopted for C++26,
increasing the version macro is not strictly necessary, because
<code>submdspan</code> (P2630) itself is a C++26 feature. We retain the
version macro increase in case the proposal is adopted after C++26,
possibly as a DR for C++26. – <em>end note</em>]</p>
<h2 data-number="9.2" id="change-mdspan.syn"><span class="header-section-number">9.2</span> Change [mdspan.syn]<a href="#change-mdspan.syn" class="self-link"></a></h2>
<blockquote>
<p>Change [mdspan.syn] (“Header <code>&lt;mdspan&gt;</code> synopsis”)
as follows.</p>
</blockquote>
<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>  struct full_extent_t { explicit full_extent_t() = default; };</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>  inline constexpr full_extent_t full_extent{};</span></code></pre></div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb8"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>  template&lt;class IndexType, size_t... Extents, class... Slices&gt;</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>    constexpr auto submdspan_canonicalize_slices(</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>      const extents&lt;IndexType, Extents...&gt;&amp; src, Slices... slices);</span></code></pre></div>

</div>
<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, class... Extents, class... SliceSpecifiers&gt;</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>    constexpr auto submdspan_extents(const extents&lt;IndexType, Extents...&gt;&amp;, SliceSpecifiers...);</span></code></pre></div>
<h2 data-number="9.3" id="change-mdspan.sub.helpers"><span class="header-section-number">9.3</span> Change [mdspan.sub.helpers]<a href="#change-mdspan.sub.helpers" class="self-link"></a></h2>
<blockquote>
<p>Change [mdspan.sub.helpers] as follows.</p>
</blockquote>
<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>template&lt;class T&gt;</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>  constexpr T <em>de-ice</em>(T val) { return val; }</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>template&lt;integral-constant-like T&gt;</span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>  constexpr auto <em>de-ice</em>(T) { return T::value; }</span></code></pre></div>
<div class="add" style="color: #00AA00">

<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>template&lt;class IndexType, class S&gt;</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>constexpr auto <em>canonical-ice</em>(S s);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<em>Constraints</em>: <code>S</code> is convertible to
<code>IndexType</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
<em>Mandates</em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(2.1)</a></span>
<code>IndexType</code> is a signed or unsigned integer type.</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(2.2)</a></span> If
<code>S</code> models <em><code>integral-constant-like</code></em> and
if <code>decltype(S::value)</code> is a signed or unsigned integer type,
then <code>S::value</code> is representable as a value of type
<code>IndexType</code>.</p></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
<em>Preconditions</em>: If <code>S</code> is a signed or unsigned
integer type, then <code>s</code> is representable as a value of type
<code>IndexType</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Returns</em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span> If
<code>S</code> models <em><code>integral-constant-like</code></em>, then
<code>constant_wrapper&lt;extents&lt;IndexType&gt;::</code><em><code>index_cast</code></em><code>(S::value), IndexType&gt;{}</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span>
otherwise,
<code>extents&lt;IndexType&gt;::</code><em><code>index-cast</code></em><code>(s)</code>.</p></li>
</ul>
<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>template&lt;class IndexType, class X, class Y&gt;</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>constexpr auto <em>subtract-ice</em>(X x, Y y) {</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>  return <em>canonical-ice</em>&lt;IndexType&gt;(y) - <em>canonical-ice</em>&lt;IndexType&gt;(x);</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>[<em>Editorial note</em>: Even though <code>submdspan</code> only
uses the difference between <code>y</code> and <code>x</code> to compute
an extent, each of <code>y</code> and <code>x</code> must be a valid
index in the slice’s extent. This is why
<em><code>subtract-ice</code></em> retains
<em><code>canonical-ice</code></em>’s precondition. – <em>end
note</em>]</p>
</div>
<div class="sourceCode" id="cb13"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb13-1"><a href="#cb13-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="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">constexpr</span> IndexType <em>first</em>_<span class="op">(</span>SliceSpecifiers<span class="op">...</span> slices<span class="op">)</span>;</span></code></pre></div>
<p>[<em>Editorial note</em>: Former paragraph 1 has been renumbered to
5. – <em>end note</em>]</p>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
<em>Mandates</em>: <code>IndexType</code> is a signed or unsigned
integer type.</p>
<p><span class="marginalizedparent"><a class="marginalized">6</a></span>
Let <span class="math inline"><em>ϕ</em><sub><em>k</em></sub></span>
denote the following value:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.1)</a></span>
<span class="math inline"><em>s</em><sub><em>k</em></sub></span> if
<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">(6.2)</a></span>
otherwise, <span class="rm" style="color: #bf0303"><del><span><code>get&lt;0&gt;(</code></span><span class="math inline"><em>s</em><sub><em>k</em></sub></span><span><code>)</code></span>
if <span class="math inline"><em>S</em><sub><em>k</em></sub></span>
models
<em><span><code>index-pair-like</code></span></em><span><code>&lt;IndexType&gt;</code></span></del></span><span class="add" style="color: #00AA00"><ins><span><code>s_k0</code></span>
if the structured binding declaration
<span><code>auto [s_k0, s_k1] =</code></span><span class="math inline"><em>s</em><sub><em>k</em></sub></span><span><code>;</code></span>
is well-formed</ins></span>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.3)</a></span>
otherwise, <em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.offset)</code>
if <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of <code>strided_slice</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.4)</a></span>
otherwise, <code>0</code>.</p></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">7</a></span>
<em>Preconditions</em>: <span class="math inline"><em>ϕ</em><sub><em>k</em></sub></span> is
representable as a value of type <code>IndexType</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">8</a></span>
<em>Returns</em>:
<code>extents&lt;IndexType&gt;::</code><em><code>index-cast</code></em><code>(</code><span class="math inline"><em>ϕ</em><sub><em>k</em></sub></span><code>)</code>.</p>
<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>template&lt;size_t k, class Extents, class... SliceSpecifiers&gt;</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>  constexpr auto <em>last</em>_(const Extents&amp; src, SliceSpecifiers... slices);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">9</a></span>
<em>Mandates</em>: <code>Extents</code> is a specialization of
<code>extents</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">10</a></span> Let
<code>index_type</code> be
<code>typename Extents::index_type</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">11</a></span> Let
<span class="math inline"><em>λ</em><sub><em>k</em></sub></span> denote
the following value:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(11.1)</a></span>
<em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>) + 1</code>
if <span class="math inline"><em>S</em><sub><em>k</em></sub></span>
models <code>convertible_to&lt;index_type&gt;</code>; otherwise</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(11.2)</a></span>
<span class="rm" style="color: #bf0303"><del><span><code>get&lt;1&gt;(</code></span><span class="math inline"><em>s</em><sub><em>k</em></sub></span><span><code>)</code></span>
if <span class="math inline"><em>S</em><sub><em>k</em></sub></span>
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><span><code>s_k1</code></span>
if the structured binding declaration
<span><code>auto [s_k0, s_k1] =</code></span><span class="math inline"><em>s</em><sub><em>k</em></sub></span><span><code>;</code></span>
is well-formed</ins></span>; otherwise</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(11.3)</a></span>
<em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.offset) +</code><em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.extent)</code>
if <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of <code>strided_slice</code>; otherwise</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(11.4)</a></span>
<code>src.extent(k)</code>.</p></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">12</a></span>
<em>Preconditions</em>: <span class="math inline"><em>λ</em><sub><em>k</em></sub></span> is
representable as a value of <code>type index_type</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">13</a></span>
<em>Returns</em>:
<code>Extents​::</code><em><code>​index-cast</code></em><code>(</code><span class="math inline"><em>λ</em><sub><em>k</em></sub></span><code>)</code>.</p>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb15"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>enum class <em>check-static-bounds-result</em> {</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>  <em>in-bounds</em>,</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>  <em>out-of-bounds</em>,</span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a>  <em>unknown</em></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a>};</span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>template&lt;size_t k, class IndexType, size_t... Exts, class... SliceSpecifiers&gt;</span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a>  constexpr <em>check-static-bounds-result</em> <em>check-static-bounds</em>(</span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a>    const extents&lt;IndexType, Exts...&gt;&amp;, SliceSpecifiers... slices);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">14</a></span>
<em>Returns</em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.1)</a></span>
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>in-bounds</code></em>
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>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.2)</a></span>
otherwise, if <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is
<em><code>integral-constant-like</code></em> and if
<code>is_convertible_v&lt;</code><span class="math inline"><em>S</em><sub><em>k</em></sub></span><code>, IndexType&gt;</code>
is <code>true</code>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.2.1)</a></span>
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>out-of-bounds</code></em>
if <em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>)</code>
&lt; 0;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.2.2)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>out-of-bounds</code></em>
if <code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code> does not equal
<code>dynamic_extent</code> and if <code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code> ≤
<em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>)</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.2.2)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>in-bounds</code></em>
if <code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code> does not equal
<code>dynamic_extent</code> and if
<em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>)</code>
&lt; <code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.2.3)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>unknown</code></em>;</p></li>
</ul></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.3)</a></span>
otherwise, if <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of <code>strided_slice</code> and if <span class="math inline"><em>S</em><sub><em>k</em></sub></span><code>::offset_type</code>
models <em><code>integral-constant-like</code></em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.3.1)</a></span>
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>out-of-bounds</code></em>
if <em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.offset)</code>
&lt; 0;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.3.2)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>out-of-bounds</code></em>
if <code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code> does not equal
<code>dynamic_extent</code> and if <code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code> &lt;
<em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.offset)</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.3.3)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>out-of-bounds</code></em>
if <span class="math inline"><em>S</em><sub><em>k</em></sub></span><code>::extent_type</code>
models <em><code>integral-constant-like</code></em> and if
<em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.offset)</code>
+ <em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.extent)</code>
&lt; 0;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.3.4)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>out-of-bounds</code></em>
if <code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code> does not equal
<code>dynamic_extent</code>, if <span class="math inline"><em>S</em><sub><em>k</em></sub></span><code>::extent_type</code>
models <em><code>integral-constant-like</code></em>, and if
<code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code> &lt;
<em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.offset)</code>
+ <em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.extent)</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.3.5)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>in-bounds</code></em>
if <code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code> does not equal
<code>dynamic_extent</code>, if <span class="math inline"><em>S</em><sub><em>k</em></sub></span><code>::extent_type</code>
models <em><code>integral-constant-like</code></em>, and if 0 ≤
<em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.offset)</code>
≤ <em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.offset)</code>
+ <em><code>de-ice</code></em><code>(</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.extent)</code>
≤ <code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.3.6)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>unknown</code></em>;</p></li>
</ul></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.4)</a></span>
otherwise, if the structured binding declaration
<code>auto [s_k0, s_k1] = declval&lt;</code><span class="math inline"><em>S</em><sub><em>k</em></sub></span><code>&gt;();</code>
is well-formed and if <code>decltype(s_k0)</code> is
<em><code>integral-constant-like</code></em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.4.1)</a></span>
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>out-of-bounds</code></em>
if <em><code>de-ice</code></em><code>(s_k0)</code> &lt; 0;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.4.2)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>out-of-bounds</code></em>
if <code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code> does not equal
<code>dynamic_extent</code> and if <code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code> &lt;
<em><code>de-ice</code></em><code>(s_k0)</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.4.3)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>out-of-bounds</code></em>
if <code>decltype(s_k1)</code> is
<em><code>integral-constant-like</code></em> and if
<em><code>de-ice</code></em><code>(s_k1)</code> &lt;
<em><code>de-ice</code></em><code>(s_k0)</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.4.4)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>out-of-bounds</code></em>
if <code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code> does not equal
<code>dynamic_extent</code>, if <code>decltype(s_k1)</code> is
<em><code>integral-constant-like</code></em>, and if
<code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code> &lt;
<em><code>de-ice</code></em><code>(s_k1)</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.4.5)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>in-bounds</code></em>
if <code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code> does not equal
<code>dynamic_extent</code>, if <code>decltype(s_k1)</code> is
<em><code>integral-constant-like</code></em>, and if 0 ≤
<em><code>de-ice</code></em><code>(s_k0)</code> ≤
<em><code>de-ice</code></em><code>(s_k1)</code> ≤
<code>Exts...[</code><span class="math inline"><em>k</em></span><code>]</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.4.6)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>unknown</code></em>;</p></li>
</ul></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(14.5)</a></span>
otherwise,
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>unknown</code></em>.</p></li>
</ul>

</div>
<div class="sourceCode" id="cb16"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>template&lt;class IndexType, size_t N, class... SliceSpecifiers&gt;</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>  constexpr array&lt;IndexType, sizeof...(SliceSpecifiers)&gt;</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>    <em>src-indices</em>(const array&lt;IndexType, N&gt;&amp; indices, SliceSpecifiers... slices);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">15</a></span>
<em>Mandates</em>: <code>IndexType</code> is a signed or unsigned
integer type.</p>
<p><span class="marginalizedparent"><a class="marginalized">16</a></span>
<em>Returns</em>: An
<code>array&lt;IndexType, sizeof...(SliceSpecifiers)&gt;</code>
<code>src_idx</code> such that for each <span class="math inline"><em>k</em></span> in the range <span class="math inline">[</span><code>0</code>,
<code>sizeof...(SliceSpecifiers)</code><span class="math inline">)</span>, <code>src_idx[</code><span class="math inline"><em>k</em></span><code>]</code> equals</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(16.1)</a></span>
<em><code>first_</code></em><code>&lt;IndexType,</code><span class="math inline"><em>k</em></span><code>&gt;(slices...)</code> for
each <span class="math inline"><em>k</em></span> where
<em><code>map-rank</code></em><code>[</code><span class="math inline"><em>k</em></span><code>]</code> equals
<code>dynamic_extent</code>,</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(16.2)</a></span>
otherwise, <em><code>first_</code></em><code>&lt;IndexType,</code><span class="math inline"><em>k</em></span><code>&gt;(slices...) + indices[</code><em><code>map-rank</code></em><code>[</code><span class="math inline"><em>k</em></span><code>]]</code>.</p></li>
</ul>
<h2 data-number="9.4" id="add-section-mdspan.sub.slices-slices-definitions-and-canonicalization"><span class="header-section-number">9.4</span> Add section
[mdspan.sub.slices], “Slices definitions and canonicalization”<a href="#add-section-mdspan.sub.slices-slices-definitions-and-canonicalization" class="self-link"></a></h2>
<blockquote>
<p>Add a new section [mdspan.sub.slices], “Slices definitions and
canonicalization,” after [mdspan.sub.helpers] and before
[mdspan.sub.extents]. The new section has the following contents.</p>
</blockquote>
<div class="add" style="color: #00AA00">
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
Given a signed or unsigned integer <code>IndexType</code>, a type <span class="math inline"><em>S</em></span> is a <em>canonical
<code>submdspan</code> index type for <code>IndexType</code></em> if
<span class="math inline"><em>S</em></span> is either
<code>IndexType</code> or
<code>constant_wrapper&lt;Value, IndexType&gt;</code> for some
<code>Value</code> of type <code>IndexType</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
Given a signed or unsigned integer <code>IndexType</code>, a type <span class="math inline"><em>S</em></span> is a <em>canonical
<code>submdspan</code> slice type for <code>IndexType</code></em> if
exactly one of the following is <code>true</code>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(2.1)</a></span>
<span class="math inline"><em>S</em></span> is
<code>full_extent_t</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(2.2)</a></span>
<span class="math inline"><em>S</em></span> is a canonical
<code>submdspan</code> index type for <code>IndexType</code>;
or</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(2.3)</a></span>
<span class="math inline"><em>S</em></span> is a specialization of
<code>strided_slice</code> where all of the following are
<code>true</code>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(2.3.1)</a></span>
<span class="math inline"><em>S</em></span><code>::offset_type</code>,
<span class="math inline"><em>S</em></span><code>::extent_type</code>,
and <span class="math inline"><em>S</em></span><code>::stride_type</code> are all
canonical <code>submdspan</code> index types for <code>IndexType</code>;
and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(2.3.2)</a></span> if
<span class="math inline"><em>S</em></span><code>::stride_type</code> is
<code>constant_wrapper&lt;Stride, IndexType&gt;</code> for some value
<code>Stride</code> of type <code>IndexType</code>, and if <span class="math inline"><em>S</em></span><code>::extent_type</code> is
<code>constant_wrapper&lt;Extent, IndexType&gt;</code> for some value
<code>Extent</code> of type <code>IndexType</code>, then either
<code>Extent</code> equals zero, or <code>Stride</code> is greater than
zero.</p></li>
</ul></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
Given an <code>extents</code> specialization <code>E</code> and a rank
index <span class="math inline"><em>k</em></span> of <code>E</code>, a
type <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is
a <em>canonical <span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice type for <code>E</code></em> if all of the
following are true:</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> is a
canonical <code>submdspan</code> slice type for
<code>E::index_type</code>, and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.2)</a></span>
<em><code>check-static-bounds</code></em><code>&lt;</code><span class="math inline"><em>k</em></span><code>&gt;(E{}, slices...)</code>
does not equal
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>out-of-bounds</code></em>.</p></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
Given a signed or unsigned integer <code>IndexType</code>, a type <span class="math inline"><em>S</em></span> is a <em>valid
<code>submdspan</code> slice type for <code>IndexType</code></em> if
exactly one of the following is <code>true</code>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span>
<code>is_convertible_v&lt;</code> <span class="math inline"><em>S</em></span> <code>, full_extent_t&gt;</code>
is <code>true</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span>
<code>is_convertible_v&lt;</code> <span class="math inline"><em>S</em></span> <code>, IndexType&gt;</code> is
<code>true</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.3)</a></span>
<span class="math inline"><em>S</em></span> is a specialization of
<code>strided_slice</code>; or</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.4)</a></span> if
<span class="math inline"><em>s</em></span> is an object of type <span class="math inline"><em>S</em></span>, then the structured binding
declaration <code>auto [s0, s1] =</code><span class="math inline"><em>s</em></span><code>;</code> is well-formed, and
each of <code>s0</code> and <code>s1</code> are convertible to
<code>IndexType</code>.</p></li>
</ul>
<p>[<em>Note 1</em>: If <span class="math inline"><em>S</em></span> is a
valid <code>submdspan</code> slice type for <code>IndexType</code>, then
it is also a canonical slice type for <code>IndexType</code>. – <em>end
note</em>]</p>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
Given an <code>extents</code> specialization <code>E</code> and a rank
index <span class="math inline"><em>k</em></span> of <code>E</code>, a
type <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is
a <em>valid <span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice type for <code>E</code></em> if all of the
following are true:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.1)</a></span>
<span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
valid <code>submdspan</code> slice type for <code>E::index_type</code>,
and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.2)</a></span>
<em><code>check-static-bounds</code></em><code>&lt;</code><span class="math inline"><em>k</em></span><code>&gt;(E{}, slices...)</code>
does not equal
<em><code>check-static-bounds-result</code></em><code>::</code><em><code>out-of-bounds</code></em>.</p></li>
</ul>
<p>[<em>Note 2</em>: If <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a valid
<span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice type for <code>E</code>, then it is also a
canonical <span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice type for <code>E</code>. – <em>end
note</em>]</p>
<p><span class="marginalizedparent"><a class="marginalized">6</a></span>
Given an <code>extents</code> specialization <code>E</code>, an object
<code>e</code> of type <code>E</code>, and a rank index <span class="math inline"><em>k</em></span> of <code>E</code>, an object <span class="math inline"><em>s</em><sub><em>k</em></sub></span> of type <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
<em>valid <span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice for <code>e</code></em> if <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a valid
<span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice type for <code>E</code>, and if all of the
following are <code>true</code>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.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>, then either</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.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.2)</a></span>
<span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>.stride</code>
&gt; 0; and</p></li>
</ul></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.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><span class="marginalizedparent"><a class="marginalized">7</a></span>
Given an <code>extents</code> specialization <code>E</code>, an object
<code>e</code> of type <code>E</code>, and a rank index <span class="math inline"><em>k</em></span> of <code>E</code>, an object <span class="math inline"><em>s</em><sub><em>k</em></sub></span> of type <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
<em>canonical <span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice for <code>e</code></em> if <span class="math inline"><em>s</em><sub><em>k</em></sub></span> is a valid
<span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice for <code>E</code>, and if <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
canonical <span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice type for <code>E</code>.</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>template&lt;class IndexType, size_t... Extents, class... Slices&gt;</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>constexpr auto submdspan_canonicalize_slices(</span>
<span id="cb17-3"><a href="#cb17-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">8</a></span>
<em>Constraints</em>: <code>sizeof...(slices)</code> equals
<code>sizeof...(Extents)</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">9</a></span>
<em>Mandates</em>: For each rank index <span class="math inline"><em>k</em></span> of <code>src</code>, <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a valid
<span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice type for
<code>extents&lt;IndexType, Extents...&gt;</code>.</p>
<p>[<em>Editorial Note</em>: This cannot be a Constraint because it is
impossible to constrain a template on whether a structured binding
declaration is well-formed. This is because a compound requirement
accepts an expression, not a declaration. – <em>end note</em>]</p>
<p><span class="marginalizedparent"><a class="marginalized">10</a></span>
<em>Preconditions</em>: For each rank index <span class="math inline"><em>k</em></span> of <code>src</code>, <span class="math inline"><em>s</em><sub><em>k</em></sub></span> is a valid
<span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice for <code>src</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">11</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">(11.1)</a></span> 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</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(11.2)</a></span>
else, if <span class="math inline"><em>S</em><sub><em>k</em></sub></span>
<code>is_convertible_v&lt;</code> <span class="math inline"><em>S</em><sub><em>k</em></sub></span>
<code>, IndexType&gt;</code> is <code>true</code>, then
<em><code>canonical-ice</code></em><code>&lt;IndexType&gt;(s)</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(11.3)</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>
<li><p><span class="marginalizedparent"><a class="marginalized">(11.4)</a></span>
else, if the structured binding declaration
<code>auto [s_k0, s_k1] =</code><span class="math inline"><em>s</em><sub><em>k</em></sub></span><code>;</code>
is well-formed, and if each of <code>s_k0</code> and <code>s_k1</code>
are convertible to <code>IndexType</code>, then
<code>strided_slice{.offset=</code><em><code>canonical-ice</code></em><code>&lt;IndexType&gt;(s_k0), .extent=</code><em><code>subtract-ice</code></em><code>&lt;IndexType&gt;(s_k0, s_k1), .stride=cw&lt;IndexType(1)&gt;}</code>.</p></li>
</ul>
<p>[<em>Note 3</em>: For each rank index <span class="math inline"><em>k</em></span> of <code>src</code>, element <span class="math inline"><em>k</em></span> of the <code>tuple</code> returned
by <code>submdspan_canonicalize_slices</code> is a canonical <span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice for <code>src</code>. – <em>end
note</em>]</p>
</div>
<h2 data-number="9.5" id="change-section-mdspan.sub.extents"><span class="header-section-number">9.5</span> Change section
[mdspan.sub.extents]<a href="#change-section-mdspan.sub.extents" class="self-link"></a></h2>
<blockquote>
<p>Change section [mdspan.sub.extents] as follows.</p>
</blockquote>
<div class="sourceCode" id="cb18"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>template&lt;class IndexType, class... Extents, class... SliceSpecifiers&gt;</span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a>  constexpr auto submdspan_extents(const extents&lt;IndexType, Extents...&gt;&amp; src,</span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a>                                   SliceSpecifiers... slices);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<em>Constraints</em>: <code>sizeof...(Slices)</code> equals
<code>Extents::rank()</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
<em>Mandates</em>: For each rank index <span class="math inline"><em>k</em></span> of <code>src.extents()</code>,
<span class="rm" style="color: #bf0303"><del>exactly one of the
following is true:</del></span><span class="add" style="color: #00AA00"><ins><span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a valid
<span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<span><code>submdspan</code></span> slice type for
<span><code>extents&lt;IndexType, Extents...&gt;</code></span>.</ins></span></p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(2.1)</a></span>
<span class="rm" style="color: #bf0303"><del><span class="math inline"><em>S</em><sub><em>k</em></sub></span> models
<span><code>convertible_to&lt;IndexType&gt;</code></span>,</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(2.2)</a></span>
<span class="rm" style="color: #bf0303"><del><span class="math inline"><em>S</em><sub><em>k</em></sub></span> models
<em><span><code>index-pair-like</code></span></em><span><code>&lt;IndexType&gt;</code></span>,</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(2.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>, or</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(2.4)</a></span>
<span class="rm" style="color: #bf0303"><del><span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of
<span><code>strided_slice</code></span>.</del></span></p></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
<em>Preconditions</em>: For each rank index <span class="math inline"><em>k</em></span> of <code>src</code>, <span class="rm" style="color: #bf0303"><del>all of the following are
<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 a valid
<span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<span><code>submdspan</code></span> slice for
<span><code>src</code></span>.</ins></span></p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.1)</a></span>
<span class="rm" style="color: #bf0303"><del>If <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of
<span><code>strided_slice</code></span></del></span></p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.1.1)</a></span>
<span class="rm" style="color: #bf0303"><del><span class="math inline"><em>s</em><sub><em>k</em></sub></span><span><code>.extent</code></span>
= 0, or</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.1.2)</a></span>
<span class="rm" style="color: #bf0303"><del><span class="math inline"><em>s</em><sub><em>k</em></sub></span><span><code>.stride</code></span>
&gt; 0</del></span></p></li>
</ul></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.2)</a></span>
<span class="rm" style="color: #bf0303"><del>0 ≤
<em><span><code>first_</code></span></em><span><code>&lt;IndexType,</code></span><span class="math inline"><em>k</em></span><span><code>&gt;(slices...)</code></span>
≤
<em><span><code>last_</code></span></em><span><code>&lt;</code></span><span class="math inline"><em>k</em></span><span><code>&gt;(src, slices...)</code></span>
≤ <span><code>src.extent(</code></span><span class="math inline"><em>k</em></span><span><code>)</code></span></del></span></p></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
Let <code>SubExtents</code> be a specialization of <code>extents</code>
such that:</p>
<h2 data-number="9.6" id="requirements-of-all-submdspan_mapping-customizations"><span class="header-section-number">9.6</span> Requirements of all
<code>submdspan_mapping</code> customizations<a href="#requirements-of-all-submdspan_mapping-customizations" class="self-link"></a></h2>
<blockquote>
<p>Right before [mdspan.sub.map.common] (“Specializations of
<code>submdspan_mapping</code>), insert a new section
[mdspan.sub.sliceable],”Sliceable layout mapping requirements,” with the
following content.</p>
</blockquote>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb19"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>layout-mapping-alike</em> LayoutMapping&gt;</span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a>constexpr auto</span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><em>submdspan-mapping-with-full-extents</em>(const LayoutMapping&amp; mapping);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<em>Constraints</em>: <code>LayoutMapping</code> meets the layout
mapping requirements ([mdspan.layout.reqmts]).</p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
<em>Returns</em>: <code>submdspan_mapping(mapping,</code> <span class="math inline"><em>P</em></span> <code>)</code>, where <span class="math inline"><em>P</em></span> is a pack of
<code>typename LayoutMapping::extents_type::rank()</code> object(s) of
type <code>full_extent_t</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>
<div class="sourceCode" id="cb20"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a>template&lt;class T&gt;</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>constexpr bool <em>is-submdspan-mapping-result</em> = false;</span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a>template&lt;class LayoutMapping&gt;</span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a>constexpr bool <em>is-submdspan-mapping-result</em>&lt;</span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a>  submdspan_mapping_result&lt;LayoutMapping&gt;&gt; = true;</span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a>template&lt;class LayoutMapping&gt;</span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a>concept <em>submdspan-mapping-result</em> =</span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a>  <em>is-submdspan-mapping-result</em>&lt;LayoutMapping&gt;;</span>
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-12"><a href="#cb20-12" aria-hidden="true" tabindex="-1"></a>template&lt;class LayoutMapping&gt;</span>
<span id="cb20-13"><a href="#cb20-13" aria-hidden="true" tabindex="-1"></a>concept <em>mapping-sliceable-with-full-extents</em> =</span>
<span id="cb20-14"><a href="#cb20-14" aria-hidden="true" tabindex="-1"></a>  requires(const LayoutMapping&amp; mapping) {</span>
<span id="cb20-15"><a href="#cb20-15" aria-hidden="true" tabindex="-1"></a>    {</span>
<span id="cb20-16"><a href="#cb20-16" aria-hidden="true" tabindex="-1"></a>      <em>submdspan-mapping-with-full-extents</em>(mapping)</span>
<span id="cb20-17"><a href="#cb20-17" aria-hidden="true" tabindex="-1"></a>    } -&gt; <em>submdspan-mapping-result</em>;</span>
<span id="cb20-18"><a href="#cb20-18" aria-hidden="true" tabindex="-1"></a>  };</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
A type <code>T</code> models
<em><code>mapping-sliceable-with-full-extents</code></em> only if
<code>T</code> is an object type that meets the layout mapping
requirements ([mdspan.layout.reqmts]).</p>
<p>[<em>Editorial Note</em>: The exposition-only concept
<em><code>mapping-sliceable-with-full-extents</code></em> exists because
it is impossible to constrain <code>submdspan</code> ([mdspan.sub.sub])
on whether <code>submdspan_mapping(src.mapping(), slices...)</code> is
well-formed. This is because it is impossible to constrain a function
template on whether one of its arguments (a member of the
<code>SliceSpecifiers</code> pack, in this case) can be on the
right-hand side of a structured binding declaration with two elements.
This, in turn, is because a structured binding declaration is a
declaration, but constraints cannot use declarations, only expressions.
— <em>end note</em>]</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>layout-mapping-alike</em> LayoutMapping, class... Slices&gt;</span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>constexpr auto</span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a><em>invoke-submdspan-mapping</em>(const LayoutMapping&amp; mapping, Slices... slices) {</span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a>  return submdspan_mapping(mapping, slices...);</span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
Let <code>M</code> be a type, and let <code>m</code> be an object of
type <code>M</code>. <code>M</code> meets the <em>sliceable layout
mapping</em> requirements if all of the following are true.</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span>
<code>M</code> models
<em><code>mapping-sliceable-with-full-extents</code></em>.</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span> the
expression
<em><code>invoke-submdspan-mapping</code></em><code>(mapping, slices...)</code>
is well-formed only if <code>sizeof...(slices)</code> equals
<code>LayoutMapping::extent_type::rank()</code>.</p></li>
</ul>
<p>[<em>Editorial Note</em>: This forms the Constraints on any
<code>submdspan_mapping</code> customization. — <em>end note</em>]</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.3)</a></span> Let
<code>sizeof...(Slices)</code> equal
<code>LayoutMapping::extents_type::rank()</code>, and for every rank
index <span class="math inline"><em>k</em></span> of
<code>LayoutMapping::extents_type</code>, let
<code>Slices...[</code><span class="math inline"><em>k</em></span><code>]</code> be a canonical <span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice type for
<code>LayoutMapping::extents_type</code>. Then,</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.3.1)</a></span>
the expression
<em><code>invoke-submdspan-mapping</code></em><code>(mapping, slices...)</code>
is well-formed; and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.3.2)</a></span>
<code>decltype(</code><em><code>invoke-submdspan-mapping</code></em><code>(mapping, slices...))</code>
is a specialization of <code>submdspan_mapping_result</code>.</p></li>
</ul></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.4)</a></span> Let
<code>sizeof...(Slices)</code> equal
<code>LayoutMapping::extents_type::rank()</code>. If there exists a
<span class="math inline"><em>k</em></span> in <span class="math inline">[0,</span> <code>sizeof...(Slices)</code><span class="math inline">)</span> such that <code>Slices...[</code><span class="math inline"><em>k</em></span><code>]</code> is not a canonical
<span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice type for
<code>LayoutMapping::extents_type</code>, then the expression
<em><code>invoke-submdspan-mapping</code></em><code>(mapping, slices...)</code>
is ill-formed.</p></li>
</ul>
<p>[<em>Note 2</em>: Restricting <code>submdspan_mapping</code>
customizations to accept only canonical slice types makes it possible
for the set of valid slice types to grow in the future. — <em>end
note</em>]</p>
<p>[<em>Editorial Note</em>: It is our belief that this wording and the
as-if rule together permit Standard layout mappings to accept any valid
<code>submdspan</code> slice types, and thus permit
<code>submdspan</code> not to canonicalize for Standard layout mappings.
Here is alternate wording if we need a special call-out for Standard
layout mappings.</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.4)</a></span> Let
<code>sizeof...(Slices)</code> equal
<code>LayoutMapping::extents_type::rank()</code>.</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.4.1)</a></span> If
<code>LayoutMapping::layout_type</code> is a layout defined in this
Standard, then if there exists a rank index <span class="math inline"><em>k</em></span> of
<code>LayoutMapping::extents_type</code> such that
<code>Slices...[</code><span class="math inline"><em>k</em></span><code>]</code> is not a valid <span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice type for
<code>LayoutMapping::extents_type</code>, then the expression
<em><code>invoke-submdspan-mapping</code></em><code>(mapping, slices...)</code>
is ill-formed.</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.4.2)</a></span>
Otherwise, if there exists a rank index <span class="math inline"><em>k</em></span> of
<code>LayoutMapping::extents_type</code> such that
<code>Slices...[</code><span class="math inline"><em>k</em></span><code>]</code> is not a canonical
<span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice type for
<code>LayoutMapping::extents_type</code>, then the expression
<em><code>invoke-submdspan-mapping</code></em><code>(mapping, slices...)</code>
is ill-formed.</p></li>
</ul></li>
</ul>
<p>— <em>end note</em>]</p>
<p>[<em>Editorial Note</em>: The above two points form the Mandates on
any <code>submdspan_mapping</code> customization. — <em>end
note</em>]</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.5)</a></span> Let
<code>sizeof...(Slices)</code> equal
<code>LayoutMapping::extents_type::rank()</code>, and for every rank
index <span class="math inline"><em>k</em></span> of
<code>LayoutMapping::extents_type</code>, let
<code>slices...[</code><span class="math inline"><em>k</em></span><code>]</code> be a canonical <span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<code>submdspan</code> slice for <code>mapping.extents()</code>. Let
<code>sub_map_offset</code> be the result of
<em><code>invoke-submdspan-mapping</code></em><code>(mapping, slices...)</code>.
Then,</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.5.1)</a></span>
<code>sub_map_offset.mapping.extents() == submdspan_extents(src.mapping(), slices...)</code>
is <code>true</code>; and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.5.2)</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></li>
</ul>
<div class="sourceCode" id="cb22"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a>sub_map_offset.mapping(I...) + sub_map_offset.offset ==</span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a>  src.mapping()(<em>src-indices</em>(array{I...}, slices...))</span></code></pre></div>
<p>is <code>true</code>.</p>
<p>[<em>Editorial Note</em>: This forms the Effects of
<code>submdspan_mapping</code>, and the Preconditions on using any
<code>submdspan_mapping</code> customization in <code>submdspan</code>
([mdspan.sub.sub]). — <em>end note</em>]</p>
</div>
<h2 data-number="9.7" id="change-mdspan.sub.map.common"><span class="header-section-number">9.7</span> Change
[mdspan.sub.map.common]<a href="#change-mdspan.sub.map.common" class="self-link"></a></h2>
<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 in [mdspan.sub.map].</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>, <span class="add" style="color: #00AA00"><ins><span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a valid
<span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<span><code>submdspan</code></span> slice type for
<span><code>extents_type</code></span>.</ins></span><span class="rm" style="color: #bf0303"><del>exactly one of the following is
<span><code>true</code></span>:</del></span></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></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></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>, or</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.4)</a></span>
<span class="rm" style="color: #bf0303"><del><span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of
<span><code>strided_slice</code></span>.</del></span></p></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Preconditions</em>: For each rank index <span class="math inline"><em>k</em></span> of <code>extents()</code>, <span class="add" style="color: #00AA00"><ins><span class="math inline"><em>s</em><sub><em>k</em></sub></span> is a valid
<span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<span><code>submdspan</code></span> slice for
<span><code>extents()</code></span>.</ins></span><span class="rm" style="color: #bf0303"><del>all of the following are
<span><code>true</code></span>:</del></span></p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span>
<span class="rm" style="color: #bf0303"><del>if <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of <span><code>strided_slice</code></span>, <span class="math inline"><em>s</em><sub><em>k</em></sub></span><span><code>.extent</code></span>
is equal to zero or <span class="math inline"><em>s</em><sub><em>k</em></sub></span><span><code>.stride</code></span>
is greater than zero; and</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span>
<span class="rm" style="color: #bf0303"><del>0 ≤
<em><span><code>first_</code></span></em><span><code>&lt;index_type,</code></span><span class="math inline"><em>k</em></span><span><code>&gt;(slices...)</code></span></del></span>
<br> <span class="rm" style="color: #bf0303"><del>0 ≤
<em><span><code>last_</code></span></em><span><code>&lt;</code></span><span class="math inline"><em>k</em></span><span><code>&gt;(extents(), slices...)</code></span></del></span>
<br> <span class="rm" style="color: #bf0303"><del>0 ≤
_<span><code>extents().extent(</code></span><span class="math inline"><em>k</em></span><span><code>)</code></span></del></span></p></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
Let <code>sub_ext</code> be the result of
<code>submdspan_extents(extents(), slices...)</code> and let
<code>SubExtents</code> be <code>decltype(sub_ext)</code>.</p>
<h2 data-number="9.8" id="submdspan-function-template"><span class="header-section-number">9.8</span> <code>submdspan</code> function
template<a href="#submdspan-function-template" class="self-link"></a></h2>
<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="cb23"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb23-1"><a href="#cb23-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>
<div class="add" style="color: #00AA00">
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<code>M</code> shall meet the sliceable layout mapping requirements.</p>
<p>[<em>Editorial Note</em>: (4) cannot be fully implemented as a
Constraint. The syntactic requirements have the effect of Mandates. The
semantic requirements have the effect of Preconditions. — <em>end
note</em>]</p>
</div>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
<em>Constraints</em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.1)</a></span>
<code>sizeof...(slices)</code> equals <code>Extents​::​rank()</code>,
and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.2)</a></span>
<span class="rm" style="color: #bf0303"><del>the expression
<span><code>submdspan_mapping(src.mapping(),</code></span>
<span><code>slices...)</code></span> is well-formed when treated as an
unevaluated operand</del></span><span class="add" style="color: #00AA00"><ins><span><code>Layout::mapping&lt;Extents&gt;</code></span>
meets the requirements of
<em><span><code>mapping-sliceable-with-full-extents</code></span></em></ins></span>.</p></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">6</a></span>
<em>Mandates</em>:</p>
<div class="rm" style="color: #bf0303">

<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.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">(6.2)</a></span>
<code>is_same_v&lt;remove_cvref_t&lt;decltype(sub_map_offset.mapping.extents())&gt;, decltype(submdspan_extents(src.mapping(), slices...))&gt;</code>
is <code>true</code>.</p></li>
</ul>

</div>
<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>, <span class="rm" style="color: #bf0303"><del>exactly one of the following is
true:</del></span><span class="add" style="color: #00AA00"><ins><span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a valid
<span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<span><code>submdspan</code></span> slice type for
<span><code>Extents</code></span>.</ins></span></p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.1.1)</a></span>
<span class="rm" style="color: #bf0303"><del><span class="math inline"><em>S</em><sub><em>k</em></sub></span> models
<span><code>convertible_to&lt;index_type&gt;</code></span>,</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.1.2)</a></span>
<span class="rm" style="color: #bf0303"><del><span class="math inline"><em>S</em><sub><em>k</em></sub></span> models
<em><span><code>index-pair-like</code></span></em><span><code>&lt;index_type&gt;</code></span>,</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.1.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>, or</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.1.4)</a></span>
<span class="rm" style="color: #bf0303"><del><span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of
<span><code>strided_slice</code></span>.</del></span></p></li>
</ul></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">7</a></span>
<em>Preconditions</em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(7.1)</a></span> For
each rank index <span class="math inline"><em>k</em></span> of
<code>src.extents()</code>, <span class="add" style="color: #00AA00"><ins><span class="math inline"><em>s</em><sub><em>k</em></sub></span> is a valid
<span class="math inline"><em>k</em><sup><em>t</em><em>h</em></sup></span>
<span><code>submdspan</code></span> slice for
<span><code>src.extents()</code></span>.</ins></span><span class="rm" style="color: #bf0303"><del>all of the following are
<span><code>true</code></span>:</del></span></p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(7.1.1)</a></span>
<span class="rm" style="color: #bf0303"><del>if <span class="math inline"><em>S</em><sub><em>k</em></sub></span> is a
specialization of
<span><code>strided_slice</code></span></del></span></p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(7.1.1.1)</a></span>
<span class="rm" style="color: #bf0303"><del><span class="math inline"><em>s</em><sub><em>k</em></sub></span><span><code>.extent</code></span>
= 0, or</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(7.1.1.2)</a></span>
<span class="rm" style="color: #bf0303"><del><span class="math inline"><em>s</em><sub><em>k</em></sub></span><span><code>.stride</code></span>
&gt; 0</del></span></p></li>
</ul></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(7.1.2)</a></span>
<span class="rm" style="color: #bf0303"><del>0 ≤
<em><span><code>first_</code></span></em><span><code>&lt;index_type,</code></span>
<span class="math inline"><em>k</em></span><span><code>&gt;(slices)</code></span>
≤
<em><span><code>last_</code></span></em><span><code>&lt;</code></span><span class="math inline"><em>k</em></span><span><code>&gt;(src.extents(), slices...)</code></span>
≤ <span><code>src.extent(k)</code></span></del></span></p></li>
</ul></li>
</ul>
<div class="rm" style="color: #bf0303">

<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(7.2)</a></span>
<code>sub_map_offset.mapping.extents() == submdspan_extents(src.mapping(), slices...)</code>
is <code>true</code>; and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(7.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="sourceCode" id="cb24"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a>sub_map_offset.mapping(I...) + sub_map_offset.offset ==</span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a>  src.mapping()(<em>src-indices</em>(array{I...}, slices...))</span></code></pre></div>
<p>is <code>true</code>.</p>
</div>
<p><span class="marginalizedparent"><a class="marginalized">8</a></span>
<em>Effects</em>: Equivalent to:</p>
<div class="add" style="color: #00AA00">

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

</div>
<div class="sourceCode" id="cb26"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a>auto sub_map_result = submdspan_mapping(src.mapping(), [canonical_]slices...);</span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a>return mdspan(src.accessor().offset(src.data(), sub_map_result.offset),</span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a>              sub_map_result.mapping,</span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a>              AccessorPolicy::offset_policy(src.accessor()));</span></code></pre></div>
<p>[<em>Editorial note</em>: We believe that the above wording permits,
but does not require canonicalization, for Standard layout mappings. If
LWG does not agree, we offer the following alternative wording.</p>
<div class="add" style="color: #00AA00">

<ul>
<li><span class="marginalizedparent"><a class="marginalized">(8.1)</a></span> If
<code>Layout</code> is one of the layouts defined in this Standard and
<code>Layout::mapping&lt;Extents&gt;</code> meets
<em><code>mapping-sliceable-with-full-extents</code></em>, then
equivalent to:</li>
</ul>
<div class="sourceCode" id="cb27"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a>auto sub_map_result = submdspan_mapping(src.mapping(), slices...);</span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a>return mdspan(src.accessor().offset(src.data(), sub_map_result.offset),</span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a>              sub_map_result.mapping,</span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a>              AccessorPolicy::offset_policy(src.accessor()));</span></code></pre></div>

</div>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(8.2)</a></span>
<span class="add" style="color: #00AA00"><ins>Otherwise,
e</ins></span><span class="rm" style="color: #bf0303"><del>E</del></span>quivalent to:</li>
</ul>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb28"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a>auto [...canonical_slices] =</span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a>  submdspan_canonicalize_slices(src.extents(), slices...);</span></code></pre></div>

</div>
<div class="sourceCode" id="cb29"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a>auto sub_map_result = submdspan_mapping(src.mapping(), <span class="add" style="color: #00AA00"><ins>canonical_</ins></span>slices...);</span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a>return mdspan(src.accessor().offset(src.data(), sub_map_result.offset),</span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a>              sub_map_result.mapping,</span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a>              AccessorPolicy::offset_policy(src.accessor()));</span></code></pre></div>
<p>– <em>end note</em>]</p>
</div>
</div>
</body>
</html>
