<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<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-18" />
  <title>Reconsider parallel `ranges::rotate_copy` and
`ranges::reverse_copy`</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%;}
      div.csl-block{margin-left: 1.5em;}
      ul.task-list{list-style: none;}
      pre > code.sourceCode { white-space: pre; position: relative; }
      pre > code.sourceCode > span { display: inline-block; 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 { } /* Normal */
      code span.al { color: #ff0000; } /* Alert */
      code span.an { } /* Annotation */
      code span.at { } /* Attribute */
      code span.bn { color: #9f6807; } /* BaseN */
      code span.bu { color: #9f6807; } /* BuiltIn */
      code span.cf { color: #00607c; } /* ControlFlow */
      code span.ch { color: #9f6807; } /* Char */
      code span.cn { } /* Constant */
      code span.co { color: #008000; font-style: italic; } /* Comment */
      code span.cv { color: #008000; font-style: italic; } /* CommentVar */
      code span.do { color: #008000; } /* Documentation */
      code span.dt { color: #00607c; } /* DataType */
      code span.dv { color: #9f6807; } /* DecVal */
      code span.er { color: #ff0000; font-weight: bold; } /* Error */
      code span.ex { } /* Extension */
      code span.fl { color: #9f6807; } /* Float */
      code span.fu { } /* Function */
      code span.im { } /* Import */
      code span.in { color: #008000; } /* Information */
      code span.kw { color: #00607c; } /* Keyword */
      code span.op { color: #af1915; } /* Operator */
      code span.ot { } /* Other */
      code span.pp { color: #6f4e37; } /* Preprocessor */
      code span.re { } /* RegionMarker */
      code span.sc { color: #9f6807; } /* SpecialChar */
      code span.ss { color: #9f6807; } /* SpecialString */
      code span.st { color: #9f6807; } /* String */
      code span.va { } /* Variable */
      code span.vs { color: #9f6807; } /* VerbatimString */
      code span.wa { color: #008000; font-weight: bold; } /* Warning */
      code.diff {color: #898887}
      code.diff span.va {color: #006e28}
      code.diff span.st {color: #bf0303}
  </style>
  <style type="text/css">
body {
margin: 5em;
font-family: serif;

hyphens: auto;
line-height: 1.35;
text-align: justify;
}
@media screen and (max-width: 30em) {
body {
margin: 1.5em;
}
}
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; }
</style>
  <link href="data:image/x-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">Reconsider parallel <code class="sourceCode cpp">ranges<span class="op">::</span>rotate_copy</code>
and <code class="sourceCode cpp">ranges<span class="op">::</span>reverse_copy</code></h1>
<table style="border:none;float:right">
  <tr>
    <td>Document #:</td>
    <td>
      P3709R0
      [<a href="https://wg21.link/P3709">Latest</a>]
      [<a href="https://wg21.link/P3709/status">Status</a>]
    </td>
  </tr>
  <tr>
    <td>Date:</td>
    <td>2025-05-18</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Project:</td>
    <td>Programming Language C++</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Audience:</td>
    <td>
      LEWG<br>
    </td>
  </tr>
  <tr>
    <td style="vertical-align:top">Reply-to:</td>
    <td>
      Ruslan Arutyunyan<br>&lt;<a href="mailto:ruslan.arutyunyan@intel.com" class="email">ruslan.arutyunyan@intel.com</a>&gt;<br>
      Alexey Kukanov<br>&lt;<a href="mailto:alexey.kukanov@intel.com" class="email">alexey.kukanov@intel.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="#introduction" id="toc-introduction"><span class="toc-section-number">1</span> Introduction<span></span></a></li>
<li><a href="#rotate_copy_design" id="toc-rotate_copy_design"><span class="toc-section-number">2</span>
<code class="sourceCode cpp">rotate_copy</code> recommended
design<span></span></a></li>
<li><a href="#reverse_copy_design" id="toc-reverse_copy_design"><span class="toc-section-number">3</span>
<code class="sourceCode cpp">reverse_copy</code> recommended
design<span></span></a></li>
<li><a href="#alternative_design" id="toc-alternative_design"><span class="toc-section-number">4</span> Alternative
design<span></span></a></li>
<li><a href="#acknowledgments" id="toc-acknowledgments"><span class="toc-section-number">5</span>
Acknowledgements<span></span></a></li>
<li><a href="#bibliography" id="toc-bibliography"><span class="toc-section-number">6</span> References<span></span></a></li>
</ul>
</div>
<h1 class="unnumbered unlisted" id="abstract">Abstract<a href="#abstract" class="self-link"></a></h1>
<p>This paper proposes reconsidering the design for parallel <code class="sourceCode cpp">ranges<span class="op">::</span>rotate_copy</code>
and <code class="sourceCode cpp">ranges<span class="op">::</span>reverse_copy</code>
in <span class="citation" data-cites="P3179R7"><a href="https://wg21.link/p3179r7" role="doc-biblioref">[P3179R7]</a></span>.</p>
<h1 data-number="1" id="introduction"><span class="header-section-number">1</span> Introduction<a href="#introduction" class="self-link"></a></h1>
<p>LEWG approved “range-as-the-output” design aspect for parallel range
algorithms proposal <span class="citation" data-cites="P3179R7"><a href="https://wg21.link/p3179r7" role="doc-biblioref">[P3179R7]</a></span>. A common questions for such
algorithms is what to return for the input when the output size is not
sufficient? For the simple cases the answer is pretty simple: we return
past the last iterator for the input and it points to the position right
after the last copied element.</p>
<p>Consider <code class="sourceCode cpp">std<span class="op">::</span>ranges<span class="op">::</span>copy</code>
as an example:</p>
<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>std<span class="op">::</span>vector input<span class="op">{</span><span class="dv">1</span>,<span class="dv">2</span>,<span class="dv">3</span>,<span class="dv">4</span>,<span class="dv">5</span><span class="op">}</span>;</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> output<span class="op">(</span><span class="dv">5</span><span class="op">)</span>; <span class="co">// output with sufficient size</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> smaller_output<span class="op">(</span><span class="dv">3</span><span class="op">)</span>; <span class="co">// output with insufficient size</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> res1 <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>copy<span class="op">(</span>std<span class="op">::</span>execution<span class="op">::</span>par, input, output<span class="op">)</span>;</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="co">// after copy invocation res.in == to input.end() is true</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> res1 <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>copy<span class="op">(</span>std<span class="op">::</span>execution<span class="op">::</span>par, input, smaller_output<span class="op">)</span>;</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="co">// after copy invocation res.in == to input.begin() + 3 is true</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="co">// res.in point to element equal 4</span></span></code></pre></div>
<p>Having that result gives us consistent behavior with serial range
algorithms, when the output size is sufficient and <em>stop point</em>
in the input when the output size is insufficient.</p>
<p>The proposed design in <span class="citation" data-cites="P3179R7"><a href="https://wg21.link/p3179r7" role="doc-biblioref">[P3179R7]</a></span> follows the same logic
(returning past the last iterator for the input) for parallel <code class="sourceCode cpp">ranges<span class="op">::</span>rotate_copy</code>
and <code class="sourceCode cpp">ranges<span class="op">::</span>reverse_copy</code>
and both algorithms use the same return type as their serial range
counterparts. It’s consistent with the rest of the parallel range
algorithms that have an output. However, it leads to interesting
consequences for the following reasons:</p>
<ul>
<li><code class="sourceCode cpp">reverse_copy</code> goes in reverse, so
past the last iterator for input is never
<code class="sourceCode cpp">last</code> (unless the output is
empty).</li>
<li><code class="sourceCode cpp">rotate_copy</code> has two “subranges”
within one range split by <code class="sourceCode cpp">middle</code>.
The past the last iterator for input is never
<code class="sourceCode cpp">last</code> again: it’s either one of the
iterators in <code class="sourceCode cpp"><span class="op">[</span>middle, last <span class="op">-</span> <span class="dv">1</span><span class="op">]</span></code>
or in <code class="sourceCode cpp"><span class="op">[</span>first, middle<span class="op">]</span></code>.</li>
</ul>
<p>The proposed design in <span class="citation" data-cites="P3179R7"><a href="https://wg21.link/p3179r7" role="doc-biblioref">[P3179R7]</a></span> looks correct in isolation
because users want to know where the algorithm stops if the output size
is not sufficient to copy all the elements from the input. The potential
problem, however, comes from the combinations of three factors:</p>
<ul>
<li>serial range algorithms always return
<code class="sourceCode cpp">last</code> for both
<code class="sourceCode cpp">reverse_copy</code> and
<code class="sourceCode cpp">rotate_copy</code>.</li>
<li>the return type for serial and parallel range algorithms is the
same, however parallel range algorithms return a different iterator for
the input even if the output size is sufficient</li>
<li>we envision serial range algorithms with “range-as-the-output”
design in the future, where it is applicable to return both <em>stop
point</em> and <code class="sourceCode cpp">last</code>, if it was
calculated.</li>
</ul>
<p>To elaborate more on serial range algorithms: the current strategy
for them (for the vast majority) is returning as much information is
possible. For example: <code class="sourceCode cpp">ranges<span class="op">::</span>for_each</code>
returns past the last iterator because that is what the algorithm
calculated besides applying a callable object element-wise. This is
useful information because users might not have such an iterator before
the algorithm call; they might only have
<code class="sourceCode cpp">sentinel</code>.</p>
<p>Furthermore, we expect serial range algorithms with
“range-as-the-output” design in the future that supports more than
<code class="sourceCode cpp">random_access_iterator</code> only. For
such algorithms it totally makes sense to return both the input <em>stop
point</em> and the input <code class="sourceCode cpp">last</code>, if
the latter was calculated. For the vast majority of algorithms with the
output the input <code class="sourceCode cpp">last</code> is either
cannot be calculated (when the output size is insufficient) or it
matches the end of input. For
<code class="sourceCode cpp">reverse_copy</code> and
<code class="sourceCode cpp">rotate_copy</code> this is not true. We
need to keep that in mind when designing these algorithms, because it
would be very unfortunate if “rangified” serial range algorithms end up
returning something different from parallel range algorithms. Thus,
parallel range algorithms design needs to accept the fact that they
should to return more information for <code class="sourceCode cpp">random_access_<span class="op">&lt;</span>iterator<span class="op">|</span>range<span class="op">&gt;</span></code>.</p>
<p>As the reminder, one of the design goals for <span class="citation" data-cites="P3179R7"><a href="https://wg21.link/p3179r7" role="doc-biblioref">[P3179R7]</a></span> was to keep the same return
type for the proposed algorithms compared to existing range algorithms,
where possible. However, there is a clear indication that we need to do
something different for <code class="sourceCode cpp">reverse_copy</code>
and <code class="sourceCode cpp">rotate_copy</code>.</p>
<h1 data-number="2" id="rotate_copy_design"><span class="header-section-number">2</span>
<code class="sourceCode cpp">rotate_copy</code> recommended design<a href="#rotate_copy_design" class="self-link"></a></h1>
<p>In <span class="citation" data-cites="P3179R7"><a href="https://wg21.link/p3179r7" role="doc-biblioref">[P3179R7]</a></span>
<code class="sourceCode cpp">rotate_copy</code> returns past the last
inserted copied element for input. By design, it never returns
<code class="sourceCode cpp">last</code>; it returns
<code class="sourceCode cpp">middle</code> for two scenarios:</p>
<ul>
<li>output size is sufficient to copy everything from input.</li>
<li>output size is empty.</li>
</ul>
<p>The raised concern is that the users might be surprised at runtime by
completely different return value when just passing an execution policy
function argument. Consider the following example:</p>
<table>
<caption><blockquote>
<p>Serial vs Parallel
<code class="sourceCode cpp">rotate_copy</code></p>
</blockquote></caption>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>iterator-as-the-output
<code class="sourceCode cpp">rotate_copy</code></strong>
</div></th>
<th><div style="text-align:center">
<strong>range-as-the-output
<code class="sourceCode cpp">rotate_copy</code></strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><div>

<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>std<span class="op">::</span>list v<span class="op">{</span><span class="dv">1</span>,<span class="dv">2</span>,<span class="dv">3</span>,<span class="dv">4</span>,<span class="dv">5</span>,<span class="dv">6</span><span class="op">}</span>;</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> out_v<span class="op">(</span><span class="dv">6</span><span class="op">)</span>;</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="co">// view is not common_range</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> view <span class="op">=</span> std<span class="op">::</span>views<span class="op">::</span>counted<span class="op">(</span>v<span class="op">.</span>begin<span class="op">()</span>, <span class="dv">6</span><span class="op">)</span>;</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="kw">static_assert</span><span class="op">(!</span>std<span class="op">::</span>ranges<span class="op">::</span>common_range<span class="op">&lt;</span><span class="kw">decltype</span><span class="op">(</span>view<span class="op">)&gt;)</span>;</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> it <span class="op">=</span> view<span class="op">.</span>begin<span class="op">()</span>;</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>ranges<span class="op">::</span>advance<span class="op">(</span>it, <span class="dv">3</span><span class="op">)</span>;</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="co">// iterator as the output</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> res <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>rotate_copy<span class="op">(</span>view, it, out_v<span class="op">.</span>begin<span class="op">())</span>;</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="co">// res.in is last</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> val <span class="op">=</span> std<span class="op">::</span>reduce<span class="op">(</span>view<span class="op">.</span>begin<span class="op">()</span>, res<span class="op">.</span>in<span class="op">)</span>;</span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a><span class="co">// val is 21</span></span></code></pre></div>

</div></td>
<td><div>

<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>std<span class="op">::</span>list v<span class="op">{</span><span class="dv">1</span>,<span class="dv">2</span>,<span class="dv">3</span>,<span class="dv">4</span>,<span class="dv">5</span>,<span class="dv">6</span><span class="op">}</span>;</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> out_v<span class="op">(</span><span class="dv">6</span><span class="op">)</span>;</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="co">// view is not common_range</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> view <span class="op">=</span> std<span class="op">::</span>views<span class="op">::</span>counted<span class="op">(</span>v<span class="op">.</span>begin<span class="op">()</span>, <span class="dv">6</span><span class="op">)</span>;</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="kw">static_assert</span><span class="op">(!</span>std<span class="op">::</span>ranges<span class="op">::</span>common_range<span class="op">&lt;</span><span class="kw">decltype</span><span class="op">(</span>view<span class="op">)&gt;)</span>;</span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> it <span class="op">=</span> view<span class="op">.</span>begin<span class="op">()</span>;</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>ranges<span class="op">::</span>advance<span class="op">(</span>it, <span class="dv">3</span><span class="op">)</span>;</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">// range as the output</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> res <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>rotate_copy<span class="op">(</span>view, it, out_v<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">// res.in is middle</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> val <span class="op">=</span> std<span class="op">::</span>reduce<span class="op">(</span>view<span class="op">.</span>begin<span class="op">()</span>, res<span class="op">.</span>in<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">// val is 6</span></span></code></pre></div>

</div></td>
</tr>
</tbody>
</table>
<p>Please note, that in the table above we don’t use algorithm overloads
with execution policy. Instead, we show the future serial range
algorithms with “range-as-the-output”. The reasons for that are:</p>
<ul>
<li>we need a non-<code class="sourceCode cpp">common_range</code>,
non-<code class="sourceCode cpp">random_access_range</code>, so that
calculating <code class="sourceCode cpp">last</code> would make
sense.</li>
<li>parallel range algorithms require
<code class="sourceCode cpp">random_access_range</code>, so the code
would fail to compile.</li>
</ul>
<p>While we could say that for
<code class="sourceCode cpp">rotate_copy</code> we would jump to the end
to return <code class="sourceCode cpp">last</code> when the output size
is sufficient there two problems with that approach:</p>
<ul>
<li>it is inconsistent with other algorithms with “range-as-the-output”.
The rest returns past the last iterator.</li>
<li>for the envisioned serial <code class="sourceCode cpp">ranges<span class="op">::</span>rotate_copy</code>
with “range-as-the-output” we calculate
<code class="sourceCode cpp">last</code> as the iterator but don’t
return that. See <a href="#introduction">Introduction</a> for more
information.</li>
</ul>
<p>So, if we don’t want to surprise users with the different behavior of
parallel <code class="sourceCode cpp">rotate_copy</code> at runtime, we
need to make it fail to compile when the returned value for existing
<code class="sourceCode cpp">rotate_copy</code> was stored and used. We
expect that users to likely store the return value from range algorithms
as <code class="sourceCode cpp"><span class="kw">auto</span></code>
type, so the names of the publicly accessible fields should also be
different for parallel range algorithms, compared to the serial
ones.</p>
<p>When we introduce “range-as-the-output” with its own bound we take
into account that the output size may be less than input size. Since
<code class="sourceCode cpp">rotate_copy</code> has two “subranges” -
from <code class="sourceCode cpp">middle</code> to
<code class="sourceCode cpp">last</code> and from
<code class="sourceCode cpp">first</code> to
<code class="sourceCode cpp">middle</code> - we want to say where the
algorithm stops for both of them, thus we propose to change the result
type to “alias-ed” <code class="sourceCode cpp">ranges<span class="op">::</span>in_in_out_result</code>.
We could introduce some new structure because currently
<code class="sourceCode cpp">in_in_out_result</code> is used for the
algorithms with two input ranges only. However, we don’t see a reason to
introduce yet another type since we think that
<code class="sourceCode cpp">in_in_out_resutl</code> has a perfect
applicability for the discovered use case.</p>
<p>The signatures are below:</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">template</span> <span class="op">&lt;</span><span class="kw">class</span> In, <span class="kw">class</span> Out<span class="op">&gt;</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">using</span> rotate_copy_truncated_result <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>in_in_out_result<span class="op">&lt;</span>In, In, Out<span class="op">&gt;</span>;</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><em>execution-policy</em> Ep, random_access_iterator I, sized_sentinel_for<span class="op">&lt;</span>I<span class="op">&gt;</span> S,</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>        random_access_iterator O, sized_sentinel_for<span class="op">&lt;</span>O<span class="op">&gt;</span> OutS<span class="op">&gt;</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">requires</span> indirectly_copyable<span class="op">&lt;</span>I, O<span class="op">&gt;</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>  ranges<span class="op">::</span>rotate_copy_truncated_result<span class="op">&lt;</span>I, O<span class="op">&gt;</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>    ranges<span class="op">::</span>rotate_copy<span class="op">(</span>Ep<span class="op">&amp;&amp;</span> exec, I first, I middle, S last, O result, OutS result_last<span class="op">)</span>;</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><em>execution-policy</em> Ep, <em>sized-random-access-range</em> R, <em>sized-random-access-range</em> OutR<span class="op">&gt;</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>  <span class="kw">requires</span> indirectly_copyable<span class="op">&lt;</span>iterator_t<span class="op">&lt;</span>R<span class="op">&gt;</span>, iterator_t<span class="op">&lt;</span>OutR<span class="op">&gt;&gt;</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>  ranges<span class="op">::</span>rotate_copy_truncated_result<span class="op">&lt;</span>borrowed_iterator_t<span class="op">&lt;</span>R<span class="op">&gt;</span>, borrowed_iterator_t<span class="op">&lt;</span>OutR<span class="op">&gt;&gt;</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>    ranges<span class="op">::</span>rotate_copy<span class="op">(</span>Ep<span class="op">&amp;&amp;</span> exec, R<span class="op">&amp;&amp;</span> r, iterator_t<span class="op">&lt;</span>R<span class="op">&gt;</span> middle, OutR<span class="op">&amp;&amp;</span> result_r<span class="op">)</span>;</span></code></pre></div>
<p>The design above addresses all the concerns in our opinion
because:</p>
<ul>
<li>for <code class="sourceCode cpp">in_out_result</code> the name for
input is <code class="sourceCode cpp">in</code> data member, while for
<code class="sourceCode cpp">in_in_out_result</code> the names for input
are <code class="sourceCode cpp">in1</code> and
<code class="sourceCode cpp">in2</code> data members, so we achieve the
goal for the the code to fail to compile when one uses the output and
switches to the parallel overload even if users store the result as
<code class="sourceCode cpp"><span class="kw">auto</span></code> type
(see a caveat below).</li>
<li>it takes into account future serial range algorithms with
“range-as-the-output” where
<code class="sourceCode cpp">rotate_copy</code> returns
<ul>
<li>both <code class="sourceCode cpp">last</code>, calculated as
iterator, and <code class="sourceCode cpp">middle</code> as the stop
point, when the output size is sufficient</li>
<li>stop points for both “subranges” (from
<code class="sourceCode cpp">middle</code> to
<code class="sourceCode cpp">last</code> and from
<code class="sourceCode cpp">first</code> to
<code class="sourceCode cpp">middle</code>) when the output size is less
than the input one.</li>
</ul></li>
</ul>
<p>Possible implementation of envisioned serial algorithm:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> rotate_copy_fn</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span>std<span class="op">::</span>forward_iterator I, std<span class="op">::</span>sentinel_for<span class="op">&lt;</span>I<span class="op">&gt;</span> S, std<span class="op">::</span>forward_iterator O, std<span class="op">::</span>sentinel_for<span class="op">&lt;</span>O<span class="op">&gt;</span> OutS<span class="op">&gt;</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> std<span class="op">::</span>indirectly_copyable<span class="op">&lt;</span>I, O<span class="op">&gt;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">constexpr</span> std<span class="op">::</span>ranges<span class="op">::</span>rotate_copy_truncated_result<span class="op">&lt;</span>I, O<span class="op">&gt;</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>        <span class="kw">operator</span><span class="op">()(</span>I first, I middle, S last, O result, OutS result_last<span class="op">)</span> <span class="kw">const</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> <span class="op">(</span>middle <span class="op">!=</span> last <span class="op">&amp;&amp;</span> result <span class="op">!=</span> result_last<span class="op">)</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>        <span class="op">{</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>            <span class="op">*</span>result <span class="op">=</span> <span class="op">*</span>middle;</span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>            <span class="op">++</span>middle;</span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>            <span class="op">++</span>result;</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> <span class="op">(</span>first <span class="op">!=</span> middle <span class="op">&amp;&amp;</span> result <span class="op">!=</span> result_last<span class="op">)</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>        <span class="op">{</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>            <span class="op">*</span>result <span class="op">=</span> <span class="op">*</span>first;</span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a>            <span class="op">++</span>first;</span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a>            <span class="op">++</span>result;</span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="op">{</span>std<span class="op">::</span>move<span class="op">(</span>middle<span class="op">)</span>, std<span class="op">::</span>move<span class="op">(</span>first<span class="op">)</span>, std<span class="op">::</span>move<span class="op">(</span>result<span class="op">)}</span>;</span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span>std<span class="op">::</span>ranges<span class="op">::</span>forward_range R, std<span class="op">::</span>ranges<span class="op">::</span>forward_range OutR<span class="op">&gt;</span></span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> std<span class="op">::</span>indirectly_copyable<span class="op">&lt;</span>std<span class="op">::</span>ranges<span class="op">::</span>iterator_t<span class="op">&lt;</span>R<span class="op">&gt;</span>, std<span class="op">::</span>ranges<span class="op">::</span>iterator_t<span class="op">&lt;</span>OutR<span class="op">&gt;&gt;</span></span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true" tabindex="-1"></a>    <span class="kw">constexpr</span> std<span class="op">::</span>ranges<span class="op">::</span>rotate_copy_truncated_result<span class="op">&lt;</span>std<span class="op">::</span>ranges<span class="op">::</span>borrowed_iterator_t<span class="op">&lt;</span>R<span class="op">&gt;</span>, std<span class="op">::</span>ranges<span class="op">::</span>borrowed_iterator_t<span class="op">&lt;</span>OutR<span class="op">&gt;&gt;</span></span>
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true" tabindex="-1"></a>        <span class="kw">operator</span><span class="op">()(</span>R<span class="op">&amp;&amp;</span> r, std<span class="op">::</span>ranges<span class="op">::</span>iterator_t<span class="op">&lt;</span>R<span class="op">&gt;</span> middle, OutR<span class="op">&amp;&amp;</span> result_r<span class="op">)</span> <span class="kw">const</span></span>
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span></span>
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="op">(*</span><span class="kw">this</span><span class="op">)(</span>std<span class="op">::</span>ranges<span class="op">::</span>begin<span class="op">(</span>r<span class="op">)</span>, std<span class="op">::</span>move<span class="op">(</span>middle<span class="op">)</span>,</span>
<span id="cb5-30"><a href="#cb5-30" aria-hidden="true" tabindex="-1"></a>                       std<span class="op">::</span>ranges<span class="op">::</span>end<span class="op">(</span>r<span class="op">)</span>, std<span class="op">::</span>ranges<span class="op">::</span>begin<span class="op">(</span>result_r<span class="op">)</span>, std<span class="op">::</span>ranges<span class="op">::</span>end<span class="op">(</span>result_r<span class="op">))</span>;</span>
<span id="cb5-31"><a href="#cb5-31" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb5-32"><a href="#cb5-32" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb5-33"><a href="#cb5-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-34"><a href="#cb5-34" aria-hidden="true" tabindex="-1"></a><span class="kw">inline</span> <span class="kw">constexpr</span> rotate_copy_fn rotate_copy <span class="op">{}</span>;</span></code></pre></div>
<p>Please note that we propose:</p>
<ul>
<li><code class="sourceCode cpp">in1</code> as a stop point in
[<code class="sourceCode cpp">middle</code>,
<code class="sourceCode cpp">last</code>].</li>
<li><code class="sourceCode cpp">in2</code> as a stop point in
[<code class="sourceCode cpp">first</code>,
<code class="sourceCode cpp">middle</code>].</li>
</ul>
<p>Even if both belong to the same input range those are just stop
points in two “subranges”, so we don’t want users to think of them as of
a valid <code class="sourceCode cpp">std<span class="op">::</span>ranges<span class="op">::</span>subrange</code>.</p>
<p>Caveat: Since C++26 we can imagine a code that compiles for both
serial range algorithms and the parallel ones even with the proposed
design, because of structured binding with a pack (<span class="citation" data-cites="P1061R10"><a href="https://wg21.link/p1061r10" role="doc-biblioref">[P1061R10]</a></span>).</p>
<p>Let’s compare:</p>
<table>
<caption><blockquote>
<p>Serial <code class="sourceCode cpp">rotate_copy</code> vs parallel
<code class="sourceCode cpp">rotate_copy</code> and structured binding
with a pack</p>
</blockquote></caption>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>serial <code class="sourceCode cpp">ranges<span class="op">::</span>rotate_copy</code></strong>
</div></th>
<th><div style="text-align:center">
<strong>Parallel <code class="sourceCode cpp">ranges<span class="op">::</span>rotate_copy</code>
with the proposed design in this paper</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><div>

<div class="sourceCode" id="cb6"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">typename</span> R, <span class="kw">typename</span> OutR<span class="op">&gt;</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> my_algorithm<span class="op">(</span>R<span class="op">&amp;&amp;</span> in, OutR<span class="op">&amp;&amp;</span> out<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> middle <span class="op">=</span> it;</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>advance<span class="op">(</span>middle, <span class="dv">3</span><span class="op">)</span>;</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">// possibly to ignore rest...</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> <span class="op">[</span>in, <span class="op">...</span>rest<span class="op">]</span> <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>rotate_copy<span class="op">(</span>in, middle,</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>                                                  std<span class="op">::</span>ranges<span class="op">::</span>begin<span class="op">(</span>out<span class="op">))</span>;</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>    <span class="co">// after a call sizeof...(rest) == 1 is true</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>

</div></td>
<td><div>

<div class="sourceCode" id="cb7"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">typename</span> R, <span class="kw">typename</span> OutR<span class="op">&gt;</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> my_algorithm<span class="op">(</span>R<span class="op">&amp;&amp;</span> in, OutR<span class="op">&amp;&amp;</span> out<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> middle <span class="op">=</span> it;</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>advance<span class="op">(</span>middle, <span class="dv">3</span><span class="op">)</span>;</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">// possibly to ignore rest...</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> <span class="op">[</span>in, <span class="op">...</span>rest<span class="op">]</span> <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>rotate_copy<span class="op">(</span>std<span class="op">::</span>execution<span class="op">::</span>par,</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>                                                  in, middle, out<span class="op">)</span>;</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>    <span class="co">// after a call sizeof...(rest) == 2 is true (with this paper)</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>

</div></td>
</tr>
</tbody>
</table>
<p>We can imagine this code if people want to only store the first data
member of the returned
<code class="sourceCode cpp"><span class="kw">struct</span></code>
object and ignore the rest. With the code comparison above it’s even
more important that <code class="sourceCode cpp">in1</code> is a stop
point from <code class="sourceCode cpp">middle</code> to
<code class="sourceCode cpp">last</code> because, as we stated above,
this code compiles and works at runtime without surprising a user.</p>
<h1 data-number="3" id="reverse_copy_design"><span class="header-section-number">3</span>
<code class="sourceCode cpp">reverse_copy</code> recommended design<a href="#reverse_copy_design" class="self-link"></a></h1>
<p>For <code class="sourceCode cpp">reverse_copy</code> the proposed
algorithm in <span class="citation" data-cites="P3179R7"><a href="https://wg21.link/p3179r7" role="doc-biblioref">[P3179R7]</a></span> always returns past the last
element (in reversed order) for input. There is no “dual meaning” for
any of returned iterators for the input. It works perfectly fine in
isolation, however two concerns come in mind:</p>
<ul>
<li>different behavior at runtime compared to serial range counterpart,
when output size is sufficient (serial one always returns
<code class="sourceCode cpp">last</code>).</li>
<li>for the envisioned serial <code class="sourceCode cpp">ranges<span class="op">::</span>reverse_copy</code>
with “range-as-the-output” we calculate
<code class="sourceCode cpp">last</code> as the iterator but don’t
return that. See <a href="#introduction">Introduction</a> for more
information.</li>
</ul>
<p>Consider the following example:</p>
<table>
<caption><blockquote>
<p>Serial vs Parallel
<code class="sourceCode cpp">reverse_copy</code></p>
</blockquote></caption>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>iterator-as-the-output
<code class="sourceCode cpp">reverse_copy</code></strong>
</div></th>
<th><div style="text-align:center">
<strong>range-as-the-output
<code class="sourceCode cpp">reverse_copy</code></strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><div>

<div class="sourceCode" id="cb8"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>list v<span class="op">{</span><span class="dv">1</span>,<span class="dv">2</span>,<span class="dv">3</span>,<span class="dv">4</span>,<span class="dv">5</span>,<span class="dv">6</span><span class="op">}</span>;</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> out_v<span class="op">(</span><span class="dv">6</span><span class="op">)</span>;</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="co">// view is not common_range</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> view <span class="op">=</span> std<span class="op">::</span>views<span class="op">::</span>counted<span class="op">(</span>v<span class="op">.</span>begin<span class="op">()</span>, <span class="dv">6</span><span class="op">)</span>;</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="kw">static_assert</span><span class="op">(!</span>std<span class="op">::</span>ranges<span class="op">::</span>common_range<span class="op">&lt;</span><span class="kw">decltype</span><span class="op">(</span>view<span class="op">)&gt;)</span>;</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a><span class="co">// iterator as the output</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> res <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>reverse_copy<span class="op">(</span>view, out_v<span class="op">.</span>begin<span class="op">())</span>;</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a><span class="co">// res.in is last</span></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> val <span class="op">=</span> std<span class="op">::</span>reduce<span class="op">(</span>view<span class="op">.</span>begin<span class="op">()</span>, res<span class="op">.</span>in<span class="op">)</span>;</span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a><span class="co">// val is 21</span></span></code></pre></div>

</div></td>
<td><div>

<div class="sourceCode" id="cb9"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>list v<span class="op">{</span><span class="dv">1</span>,<span class="dv">2</span>,<span class="dv">3</span>,<span class="dv">4</span>,<span class="dv">5</span>,<span class="dv">6</span><span class="op">}</span>;</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> out_v<span class="op">(</span><span class="dv">6</span><span class="op">)</span>;</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="co">// view is not common_range</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> view <span class="op">=</span> std<span class="op">::</span>views<span class="op">::</span>counted<span class="op">(</span>v<span class="op">.</span>begin<span class="op">()</span>, <span class="dv">6</span><span class="op">)</span>;</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a><span class="kw">static_assert</span><span class="op">(!</span>std<span class="op">::</span>ranges<span class="op">::</span>common_range<span class="op">&lt;</span><span class="kw">decltype</span><span class="op">(</span>view<span class="op">)&gt;)</span>;</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a><span class="co">// range as the output</span></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> res <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>reverse_copy<span class="op">(</span>view, it, out_v<span class="op">)</span>;</span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a><span class="co">// res.in is middle</span></span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> val <span class="op">=</span> std<span class="op">::</span>reduce<span class="op">(</span>view<span class="op">.</span>begin<span class="op">()</span>, res<span class="op">.</span>in<span class="op">)</span>;</span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a><span class="co">// val is 0</span></span></code></pre></div>

</div></td>
</tr>
</tbody>
</table>
<p>Eventually, the story is similar with
<code class="sourceCode cpp">rotate_copy</code>. All the following
ruminations also apply for parallel
<code class="sourceCode cpp">reverse_copy</code>:</p>
<ul>
<li>new return type vs
<code class="sourceCode cpp">in_in_out_result</code></li>
<li>returning two different points of input range rather then a valid
<code class="sourceCode cpp">std<span class="op">::</span>ranges<span class="op">::</span>subrange</code></li>
<li>structured binding with a parameter pack</li>
</ul>
<p>The solution is also similar. We propose to return “alias-ed” <code class="sourceCode cpp">ranges<span class="op">::</span>in_in_out_result</code>:</p>
<ul>
<li><code class="sourceCode cpp">in1</code> is always
<code class="sourceCode cpp">last</code>.</li>
<li><code class="sourceCode cpp">in2</code> represents a stop
point.</li>
</ul>
<p>The signatures are below:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> In, <span class="kw">class</span> Out<span class="op">&gt;</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="kw">using</span> reverse_copy_truncated_result <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>in_in_out_result<span class="op">&lt;</span>In, In, Out<span class="op">&gt;</span>;</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><em>execution-policy</em> Ep, random_access_iterator I, sized_sentinel_for<span class="op">&lt;</span>I<span class="op">&gt;</span> S,</span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>        random_access_iterator O, sized_sentinel_for<span class="op">&lt;</span>O<span class="op">&gt;</span> OutS<span class="op">&gt;</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">requires</span> indirectly_copyable<span class="op">&lt;</span>I, O<span class="op">&gt;</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>  ranges<span class="op">::</span>reverse_copy_truncated_result<span class="op">&lt;</span>I, O<span class="op">&gt;</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>    ranges<span class="op">::</span>reverse_copy<span class="op">(</span>Ep<span class="op">&amp;&amp;</span> exec, I first, S last, O result, OutS result_last<span class="op">)</span>;</span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><em>execution-policy</em> Ep, <em>sized-random-access-range</em> R, <em>sized-random-access-range</em> OutR<span class="op">&gt;</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a>  <span class="kw">requires</span> indirectly_copyable<span class="op">&lt;</span>iterator_t<span class="op">&lt;</span>R<span class="op">&gt;</span>, iterator_t<span class="op">&lt;</span>OutR<span class="op">&gt;&gt;</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a>  ranges<span class="op">::</span>reverse_copy_truncated_result<span class="op">&lt;</span>borrowed_iterator_t<span class="op">&lt;</span>R<span class="op">&gt;</span>, borrowed_iterator_t<span class="op">&lt;</span>OutR<span class="op">&gt;&gt;</span></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a>    ranges<span class="op">::</span>reverse_copy<span class="op">(</span>Ep<span class="op">&amp;&amp;</span> exec, R<span class="op">&amp;&amp;</span> r, OutR<span class="op">&amp;&amp;</span> result_r<span class="op">)</span>;</span></code></pre></div>
<p>Possible implementation of envisioned serial algorithm:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> reverse_copy_fn</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span>std<span class="op">::</span>bidirectional_iterator I, std<span class="op">::</span>sentinel_for<span class="op">&lt;</span>I<span class="op">&gt;</span> S, std<span class="op">::</span>forward_iterator O, std<span class="op">::</span>sentinel_for<span class="op">&lt;</span>O<span class="op">&gt;</span> OutS<span class="op">&gt;</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> std<span class="op">::</span>indirectly_copyable<span class="op">&lt;</span>I, O<span class="op">&gt;</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">constexpr</span> std<span class="op">::</span>ranges<span class="op">::</span>reverse_copy_truncated_result<span class="op">&lt;</span>I, O<span class="op">&gt;</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>        <span class="kw">operator</span><span class="op">()(</span>I first, S last, O result, OutS result_last<span class="op">)</span> <span class="kw">const</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>        <span class="kw">auto</span> res <span class="op">=</span> std<span class="op">::</span>ranges<span class="op">::</span>next<span class="op">(</span>first, last<span class="op">)</span>;</span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a>        <span class="kw">auto</span> last_iter <span class="op">=</span> res;</span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> <span class="op">(</span>last_iter <span class="op">!=</span> first <span class="op">&amp;&amp;</span> result <span class="op">!=</span> result_last<span class="op">)</span></span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a>        <span class="op">{</span></span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a>          <span class="op">*</span>result <span class="op">=</span> <span class="op">*--</span>last_iter;</span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a>            <span class="op">++</span>result;</span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="op">{</span>last_iter, res, result<span class="op">}</span>;</span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span>std<span class="op">::</span>ranges<span class="op">::</span>bidirectional_range R, std<span class="op">::</span>ranges<span class="op">::</span>forward_range OutR<span class="op">&gt;</span></span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> std<span class="op">::</span>indirectly_copyable<span class="op">&lt;</span>std<span class="op">::</span>ranges<span class="op">::</span>iterator_t<span class="op">&lt;</span>R<span class="op">&gt;</span>, std<span class="op">::</span>ranges<span class="op">::</span>iterator_t<span class="op">&lt;</span>OutR<span class="op">&gt;&gt;</span></span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true" tabindex="-1"></a>    <span class="kw">constexpr</span> std<span class="op">::</span>ranges<span class="op">::</span>reverse_copy_truncated_result<span class="op">&lt;</span>std<span class="op">::</span>ranges<span class="op">::</span>borrowed_iterator_t<span class="op">&lt;</span>R<span class="op">&gt;</span>, std<span class="op">::</span>ranges<span class="op">::</span>borrowed_iterator_t<span class="op">&lt;</span>OutR<span class="op">&gt;&gt;</span></span>
<span id="cb11-23"><a href="#cb11-23" aria-hidden="true" tabindex="-1"></a>        <span class="kw">operator</span><span class="op">()(</span>R<span class="op">&amp;&amp;</span> r, OutR<span class="op">&amp;&amp;</span> result_r<span class="op">)</span> <span class="kw">const</span></span>
<span id="cb11-24"><a href="#cb11-24" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span></span>
<span id="cb11-25"><a href="#cb11-25" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="op">(*</span><span class="kw">this</span><span class="op">)(</span>std<span class="op">::</span>ranges<span class="op">::</span>begin<span class="op">(</span>r<span class="op">)</span>, std<span class="op">::</span>ranges<span class="op">::</span>end<span class="op">(</span>r<span class="op">)</span>,</span>
<span id="cb11-26"><a href="#cb11-26" aria-hidden="true" tabindex="-1"></a>                       std<span class="op">::</span>ranges<span class="op">::</span>begin<span class="op">(</span>result_r<span class="op">)</span>, std<span class="op">::</span>ranges<span class="op">::</span>end<span class="op">(</span>result_r<span class="op">))</span>;</span>
<span id="cb11-27"><a href="#cb11-27" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb11-28"><a href="#cb11-28" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb11-29"><a href="#cb11-29" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-30"><a href="#cb11-30" aria-hidden="true" tabindex="-1"></a><span class="kw">inline</span> <span class="kw">constexpr</span> reverse_copy_fn reverse_copy <span class="op">{}</span>;</span></code></pre></div>
<h1 data-number="4" id="alternative_design"><span class="header-section-number">4</span> Alternative design<a href="#alternative_design" class="self-link"></a></h1>
<p>There are the following alternatives to the proposed design:</p>
<ul>
<li>Keep <span class="citation" data-cites="P3179R7"><a href="https://wg21.link/p3179r7" role="doc-biblioref">[P3179R7]</a></span> status quo. The concerns are:
<ul>
<li>The different behavior at runtime, when switching the code to
parallel algorithms.</li>
<li>No calculated <code class="sourceCode cpp">last</code> for future
serial range algorithms with “range-as-the-output”.</li>
</ul></li>
<li>Keep <code class="sourceCode cpp">reverse_copy</code> as proposed in
<span class="citation" data-cites="P3179R7"><a href="https://wg21.link/p3179r7" role="doc-biblioref">[P3179R7]</a></span>, return
<code class="sourceCode cpp">last</code> for parallel
<code class="sourceCode cpp">rotate_copy</code> when the output is
sufficient. The concerns are:
<ul>
<li><code class="sourceCode cpp">reverse_copy</code> remains the same,
the concerns from the previous bullets apply.</li>
<li><code class="sourceCode cpp">rotate_copy</code> becomes inconsistent
with other parallel range algorithms with “range-as-the-output” because
it “jumps” to last element instead of return past the last one.</li>
<li>The parallel <code class="sourceCode cpp">rotate_copy</code>
calculates <code class="sourceCode cpp">last</code> but does not return
it, so it likely means a different return type for serial “rangified”
<code class="sourceCode cpp">rotate_copy</code> in the future.</li>
</ul></li>
</ul>
<p>If this proposal does not have a consensus, authors’ preference is to
remove parallel <code class="sourceCode cpp">reverse_copy</code> and
<code class="sourceCode cpp">rotate_copy</code> from <span class="citation" data-cites="P3179R7"><a href="https://wg21.link/p3179r7" role="doc-biblioref">[P3179R7]</a></span> and add them later.</p>
<h1 data-number="5" id="acknowledgments"><span class="header-section-number">5</span> Acknowledgements<a href="#acknowledgments" class="self-link"></a></h1>
<ul>
<li>Thanks to Jonathan Mueller for having a fruitful discussion on
<code class="sourceCode cpp">reverse_copy</code> and
<code class="sourceCode cpp">rotate_copy</code> return type.</li>
</ul>
<h1 data-number="6" id="bibliography"><span class="header-section-number">6</span> References<a href="#bibliography" class="self-link"></a></h1>
<div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="1" role="doc-bibliography">
<div id="ref-P1061R10" class="csl-entry" role="doc-biblioentry">
[P1061R10] Barry Revzin, Jonathan Wakely. 2024-11-24. Structured
Bindings can introduce a Pack. <a href="https://wg21.link/p1061r10"><div class="csl-block">https://wg21.link/p1061r10</div></a>
</div>
<div id="ref-P3179R7" class="csl-entry" role="doc-biblioentry">
[P3179R7] Ruslan Arutyunyan, Alexey Kukanov, Bryce Adelstein Lelbach.
2025-02-28. C++ parallel range algorithms. <a href="https://wg21.link/p3179r7"><div class="csl-block">https://wg21.link/p3179r7</div></a>
</div>
</div>
</div>
</div>
</body>
</html>
