<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Structured Bindings can introduce a Pack</title>
<style type="text/css">html {
	position: relative;
	max-width: 1024px;
	height: 100%;
}
body {
	font-family: Helvetica, arial, sans-serif;
	font-size: 14px;
	line-height: 1.6;
	padding-top: 10px;
	padding-bottom: 10px;
	background-color: white;
	padding: 30px;
}
body>*:first-child {
	margin-top: 0 !important;
}
body>*:last-child {
	margin-bottom: 0 !important;
}
a {
	color: #4183C4;
}
a.absent {
	color: #cc0000;
}
a.anchor {
	display: block;
	padding-left: 30px;
	margin-left: -30px;
	cursor: pointer;
	position: absolute;
	top: 0;
	left: 0;
	bottom: 0;
}
h1, h2, h3, h4, h5, h6 {
	margin: 20px 0 10px;
	padding: 0;
	font-weight: bold;
	-webkit-font-smoothing: antialiased;
	cursor: text;
	position: relative;
}
h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
	background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA09pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoMTMuMCAyMDEyMDMwNS5tLjQxNSAyMDEyLzAzLzA1OjIxOjAwOjAwKSAgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUM2NjlDQjI4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUM2NjlDQjM4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QzY2OUNCMDg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QzY2OUNCMTg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsQhXeAAAABfSURBVHjaYvz//z8DJYCRUgMYQAbAMBQIAvEqkBQWXI6sHqwHiwG70TTBxGaiWwjCTGgOUgJiF1J8wMRAIUA34B4Q76HUBelAfJYSA0CuMIEaRP8wGIkGMA54bgQIMACAmkXJi0hKJQAAAABJRU5ErkJggg==) no-repeat 10px center;
	text-decoration: none;
}
h1 tt, h1 code {
	font-size: inherit;
}
h2 tt, h2 code {
	font-size: inherit;
}
h3 tt, h3 code {
	font-size: inherit;
}
h4 tt, h4 code {
	font-size: inherit;
}
h5 tt, h5 code {
	font-size: inherit;
}
h6 tt, h6 code {
	font-size: inherit;
}
h1 {
	font-size: 28px;
	color: black;
}
h2 {
	font-size: 24px;
	border-bottom: 1px solid #cccccc;
	color: black;
}
h3 {
	font-size: 18px;
}
h4 {
	font-size: 16px;
}
h5 {
	font-size: 14px;
}
h6 {
	color: #777777;
	font-size: 14px;
}
p, blockquote, ol, dl, li, table, pre {
	margin: 15px 0;
}
hr {
	background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAYAAACtBE5DAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OENDRjNBN0E2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OENDRjNBN0I2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Q0NGM0E3ODY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Q0NGM0E3OTY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqqezsUAAAAfSURBVHjaYmRABcYwBiM2QSA4y4hNEKYDQxAEAAIMAHNGAzhkPOlYAAAAAElFTkSuQmCC) repeat-x 0 0;
	border: 0 none;
	color: #cccccc;
	height: 4px;
	padding: 0;
}
body>h2:first-child {
	margin-top: 0;
	padding-top: 0;
}
body>h1:first-child {
	margin-top: 0;
	padding-top: 0;
}
body>h1:first-child+h2 {
	margin-top: 0;
	padding-top: 0;
}
body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
	margin-top: 0;
	padding-top: 0;
}
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
	margin-top: 0;
	padding-top: 0;
}
h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
	margin-top: 0;
}
li p.first {
	display: inline-block;
}
li {
	margin: 0;
}
ol {
	padding-left: 30px;
    margin: 5px;
    counter-reset: item;
    margin-left: -1px;
    margin-bottom: -1px;
    margin-top: -1px;
}
ol > li {
    counter-increment: item;
    margin-bottom: -1px;
    margin-top: -1px;    
}
ol ol > li {
    display: block;
    margin-bottom: -1px;
    margin-top: -1px;    
}
ol ol > li:before {
    content: counters(item, ".") ". ";
    margin-left: -30px;
    margin-bottom: -1px;
    margin-top: -1px;    
}
ul :first-child, ol :first-child {
	margin-top: 0;
}
ul ul { 
    margin-left: -15px;
}
dl {
	padding: 0;
}
dl dt {
	font-size: 14px;
	font-weight: bold;
	font-style: italic;
	padding: 0;
	margin: 15px 0 5px;
}
dl dt:first-child {
	padding: 0;
}
dl dt> :first-child {
	margin-top: 0;
}
dl dt> :last-child {
	margin-bottom: 0;
}
dl dd {
	margin: 0 0 15px;
	padding: 0 15px;
}
dl dd> :first-child {
	margin-top: 0;
}
dl dd> :last-child {
	margin-bottom: 0;
}
blockquote {
	border-left: 4px solid #dddddd;
	padding: 0 15px;
	color: #777777;
}
blockquote> :first-child {
	margin-top: 0;
}
blockquote> :last-child {
	margin-bottom: 0;
}
table {
	padding: 0;
	border-collapse: collapse;
}
table tr {
	border-top: 1px solid #cccccc;
	background-color: white;
	margin: 0;
	padding: 0;
}
table tr:nth-child(2n) {
	background-color: #f8f8f8;
}
table tr th {
	font-weight: bold;
	border: 1px solid #cccccc;
	margin: 0;
	padding: 6px 13px;
}
table tr td {
	border: 1px solid #cccccc;
	margin: 0;
	padding: 6px 13px;
}
table tr th :first-child, table tr td :first-child {
	margin-top: 0;
}
table tr th :last-child, table tr td :last-child {
	margin-bottom: 0;
}
td {
	vertical-align: top;
}
img {
	max-width: 100%;
}
span.frame {
	display: block;
	overflow: hidden;
}
span.frame>span {
	border: 1px solid #dddddd;
	display: block;
	float: left;
	overflow: hidden;
	margin: 13px 0 0;
	padding: 7px;
	width: auto;
}
span.frame span img {
	display: block;
	float: left;
}
span.frame span span {
	clear: both;
	color: #333333;
	display: block;
	padding: 5px 0 0;
}
span.align-center {
	display: block;
	overflow: hidden;
	clear: both;
}
span.align-center>span {
	display: block;
	overflow: hidden;
	margin: 13px auto 0;
	text-align: center;
}
span.align-center span img {
	margin: 0 auto;
	text-align: center;
}
span.align-right {
	display: block;
	overflow: hidden;
	clear: both;
}
span.align-right>span {
	display: block;
	overflow: hidden;
	margin: 13px 0 0;
	text-align: right;
}
span.align-right span img {
	margin: 0;
	text-align: right;
}
span.float-left {
	display: block;
	margin-right: 13px;
	overflow: hidden;
	float: left;
}
span.float-left span {
	margin: 13px 0 0;
}
span.float-right {
	display: block;
	margin-left: 13px;
	overflow: hidden;
	float: right;
}
span.float-right>span {
	display: block;
	overflow: hidden;
	margin: 13px auto 0;
	text-align: right;
}
code, tt {
	margin: 0 2px;
	padding: 0 5px;
	white-space: nowrap;
	border: 1px solid #eaeaea;
	background-color: #f8f8f8;
	border-radius: 3px;
}
pre code {
	margin: 0;
	padding: 0;
	white-space: pre;
	border: none;
	background: transparent;
}
.highlight pre {
	background-color: #f8f8f8;
	border: 1px solid #cccccc;
	font-size: 13px;
	line-height: 19px;
	overflow: auto;
	padding: 6px 10px;
	border-radius: 3px;
}
pre {
	background-color: #f8f8f8;
	border: 1px solid #cccccc;
	font-size: 13px;
	line-height: 19px;
	overflow: auto;
    overflow-x: hidden;
    overflow-y: hidden;
    padding: 6px 10px;
	border-radius: 3px;
}
pre code, pre tt {
	background-color: transparent;
	border: none;
}
sup {
	font-size: 0.83em;
	vertical-align: super;
	line-height: 0;
}
kbd {
	display: inline-block;
	padding: 3px 5px;
	font-size: 11px;
	line-height: 10px;
	color: #555;
	vertical-align: middle;
	background-color: #fcfcfc;
	border: solid 1px #ccc;
	border-bottom-color: #bbb;
	border-radius: 3px;
	box-shadow: inset 0 -1px 0 #bbb
}
* {
	-webkit-print-color-adjust: exact;
}
ins {
	color: #00A000
}
del {
	color: #A00000
}
</style><style type="text/css">
/**
	* prism.js default theme for JavaScript, CSS and HTML
	* Based on dabblet (http://dabblet.com)
	* @author Lea Verou
	*/

code[class*="language-"], pre[class*="language-"] {
	color: black;
	background: none;
	text-shadow: 0 1px white;
	font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
	font-size: 11px;
	text-align: left;
	white-space: pre;
	word-spacing: normal;
	word-break: normal;
	word-wrap: normal;
	line-height: 1.5;
	-moz-tab-size: 4;
	-o-tab-size: 4;
	tab-size: 4;
	-webkit-hyphens: none;
	-moz-hyphens: none;
	-ms-hyphens: none;
	hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
	text-shadow: none;
	background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection, code[class*="language-"]::selection, code[class*="language-"] ::selection {
	text-shadow: none;
	background: #b3d4fc;
}
@media print {
	code[class*="language-"], pre[class*="language-"] {
		text-shadow: none;
	}
}

/* Code blocks */

pre[class*="language-"] {
	padding: 1em;
	margin: .5em 0;
	overflow: auto;
    overflow-x: hidden;
    overflow-y: hidden;
}
:not(pre)>code[class*="language-"], pre[class*="language-"] {
	background: #f8f8f8;
}

/* Inline code */

:not(pre)>code[class*="language-"] {
	padding: .1em;
	border-radius: .3em;
	white-space: normal;
}
.token.comment, .token.prolog, .token.doctype, .token.cdata {
	color: slategray;
}
.token.punctuation {
	color: #999;
}
.namespace {
	opacity: .7;
}
.token.property, .token.tag, .token.boolean, .token.number, .token.constant, .token.symbol, .token.deleted {
	color: #905;
}
.token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.inserted {
	color: #690;
}
.token.operator {
	color: #a67f59;
}
.token.entity, .token.url, .language-css .token.string, .style .token.string {
	color: #a67f59;
	background: hsla(0, 0%, 100%, .5);
}
.token.atrule, .token.attr-value, .token.keyword {
	color: #07a;
}
.token.function {
	color: #DD4A68;
}
.token.regex, .token.important, .token.variable {
	color: #e90;
}
.token.important, .token.bold {
	font-weight: bold;
}
.token.italic {
	font-style: italic;
}
.token.entity {
	cursor: help;
}
</style>

<script type="text/javascript">
var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function(e){var t=n.util.type(e);switch(t){case"Object":var a={};for(var r in e)e.hasOwnProperty(r)&&(a[r]=n.util.clone(e[r]));return a;case"Array":return e.map&&e.map(function(e){return n.util.clone(e)})}return e}},languages:{extend:function(e,t){var a=n.util.clone(n.languages[e]);for(var r in t)a[r]=t[r];return a},insertBefore:function(e,t,a,r){r=r||n.languages;var l=r[e];if(2==arguments.length){a=arguments[1];for(var i in a)a.hasOwnProperty(i)&&(l[i]=a[i]);return l}var o={};for(var s in l)if(l.hasOwnProperty(s)){if(s==t)for(var i in a)a.hasOwnProperty(i)&&(o[i]=a[i]);o[s]=l[s]}return n.languages.DFS(n.languages,function(t,n){n===r[e]&&t!=e&&(this[t]=o)}),r[e]=o},DFS:function(e,t,a,r){r=r||{};for(var l in e)e.hasOwnProperty(l)&&(t.call(e,l,e[l],a||l),"Object"!==n.util.type(e[l])||r[n.util.objId(e[l])]?"Array"!==n.util.type(e[l])||r[n.util.objId(e[l])]||(r[n.util.objId(e[l])]=!0,n.languages.DFS(e[l],t,l,r)):(r[n.util.objId(e[l])]=!0,n.languages.DFS(e[l],t,null,r)))}},plugins:{},highlightAll:function(e,t){var a={callback:t,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};n.hooks.run("before-highlightall",a);for(var r,l=a.elements||document.querySelectorAll(a.selector),i=0;r=l[i++];)n.highlightElement(r,e===!0,a.callback)},highlightElement:function(t,a,r){for(var l,i,o=t;o&&!e.test(o.className);)o=o.parentNode;o&&(l=(o.className.match(e)||[,""])[1],i=n.languages[l]),t.className=t.className.replace(e,"").replace(/\s+/g," ")+" language-"+l,o=t.parentNode,/pre/i.test(o.nodeName)&&(o.className=o.className.replace(e,"").replace(/\s+/g," ")+" language-"+l);var s=t.textContent,u={element:t,language:l,grammar:i,code:s};if(!s||!i)return n.hooks.run("complete",u),void 0;if(n.hooks.run("before-highlight",u),a&&_self.Worker){var c=new Worker(n.filename);c.onmessage=function(e){u.highlightedCode=e.data,n.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(u.element),n.hooks.run("after-highlight",u),n.hooks.run("complete",u)},c.postMessage(JSON.stringify({language:u.language,code:u.code,immediateClose:!0}))}else u.highlightedCode=n.highlight(u.code,u.grammar,u.language),n.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(t),n.hooks.run("after-highlight",u),n.hooks.run("complete",u)},highlight:function(e,t,r){var l=n.tokenize(e,t);return a.stringify(n.util.encode(l),r)},tokenize:function(e,t){var a=n.Token,r=[e],l=t.rest;if(l){for(var i in l)t[i]=l[i];delete t.rest}e:for(var i in t)if(t.hasOwnProperty(i)&&t[i]){var o=t[i];o="Array"===n.util.type(o)?o:[o];for(var s=0;s<o.length;++s){var u=o[s],c=u.inside,g=!!u.lookbehind,h=!!u.greedy,f=0,d=u.alias;u=u.pattern||u;for(var p=0;p<r.length;p++){var m=r[p];if(r.length>e.length)break e;if(!(m instanceof a)){u.lastIndex=0;var y=u.exec(m),v=1;if(!y&&h&&p!=r.length-1){var b=r[p+1].matchedStr||r[p+1],k=m+b;if(p<r.length-2&&(k+=r[p+2].matchedStr||r[p+2]),u.lastIndex=0,y=u.exec(k),!y)continue;var w=y.index+(g?y[1].length:0);if(w>=m.length)continue;var _=y.index+y[0].length,P=m.length+b.length;if(v=3,P>=_){if(r[p+1].greedy)continue;v=2,k=k.slice(0,P)}m=k}if(y){g&&(f=y[1].length);var w=y.index+f,y=y[0].slice(f),_=w+y.length,S=m.slice(0,w),O=m.slice(_),j=[p,v];S&&j.push(S);var A=new a(i,c?n.tokenize(y,c):y,d,y,h);j.push(A),O&&j.push(O),Array.prototype.splice.apply(r,j)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(t)}}},a=n.Token=function(e,t,n,a,r){this.type=e,this.content=t,this.alias=n,this.matchedStr=a||null,this.greedy=!!r};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var l={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}n.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+"</"+l.tag+">"},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,l=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",n.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
</script>

<script type="text/javascript">
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:{pattern:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/};
</script>

<script type="text/javascript">
Prism.languages.c=Prism.languages.extend("clike",{keyword:/\b(asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/,operator:/\-[>-]?|\+\+?|!=?|<<?=?|>>?=?|==?|&&?|\|?\||[~^%?*\/]/,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)[ful]*\b/i}),Prism.languages.insertBefore("c","string",{macro:{pattern:/(^\s*)#\s*[a-z]+([^\r\n\\]|\\.|\\(?:\r\n?|\n))*/im,lookbehind:!0,alias:"property",inside:{string:{pattern:/(#\s*include\s*)(<.+?>|("|')(\\?.)+?\3)/,lookbehind:!0},directive:{pattern:/(#\s*)\b(define|elif|else|endif|error|ifdef|ifndef|if|import|include|line|pragma|undef|using)\b/,lookbehind:!0,alias:"keyword"}}},constant:/\b(__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|stdin|stdout|stderr)\b/}),delete Prism.languages.c["class-name"],delete Prism.languages.c["boolean"];
</script>

<script type="text/javascript">
Prism.languages.cpp=Prism.languages.extend("c",{keyword:/\b(alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|class|compl|concept|const|constexpr|const_cast|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,"boolean":/\b(true|false)\b/,operator:/[-+]{1,2}|!=?|<{1,2}=?|>{1,2}=?|\->|:{1,2}|={1,2}|\^|~|%|&{1,2}|\|?\||\?|\*|\/|\b(and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/}),Prism.languages.insertBefore("cpp","keyword",{"class-name":{pattern:/(class\s+)[a-z0-9_]+/i,lookbehind:!0}});
</script>

<script type="text/javascript">
$('tblcode').replaceWith(function(){
    return $("<pre style=\"background:transparent;border:none\"><code class=\"language-cpp\">" + $(this).text() + "</code></pre>");
});
</script>

</head>
<body>
<address align=right>
Document Number: P1061R0 <br />
Date: 2018-05-01 <br />
Audience: EWG <br />
Reply-To: Barry Revzin, barry dot revzin at gmail dot com<br />Jonathan Wakely, jonathan dot wakely at gmail dot com <br />
</address>
<hr /><h1 align=center>Structured Bindings can introduce a Pack</h1>
<h2>Contents</h2>
<div class="toc">
<ol>
<li><a href="#motivation">Motivation</a></li>
<li><a href="#proposal">Proposal</a></li>
<li><a href="#wording">Wording</a></li>
<li><a href="#acknowledgements">Acknowledgements</a></li>
<li><a href="#references">References</a></li>
</ol>
</div>

<h2 id="motivation">1. Motivation</h2>
<p>Function parameter packs and tuples are conceptually very similar. Both are heterogeneous sequences of objects. Some problems are easier to solve with a parameter pack, some are easier to solve with a <code class="language-cpp">tuple</code>. Today, it's trivial to convert a pack to a <code class="language-cpp">tuple</code>, but it's somewhat more involved to convert a <code class="language-cpp">tuple</code> to a pack. You have to go through <a href="https://wg21.link/n3915"><code class="language-cpp">std::apply()</code></a>:</p>
<pre class="codehilite"><code class="language-cpp">std::tuple&lt;A, B, C&gt; tup = ...;
std::apply([&amp;](auto&amp;&amp;... elems){
    // now I have a pack
}, tup);</code></pre>


<p>This is great for cases where we just need to call a [non-overloaded] function or function object, but rapidly becomes much more awkward as we dial up the complexity. Not to mention if I want to return from the outer scope based on what these elements have to be.</p>
<p>How do we compute the dot product of two <code class="language-cpp">tuple</code>s? It's a choose your own adventure of awkward choices:</p>
<table style="width:100%">
<tr>
<th style="width:50%">
<p>Nested <code class="language-cpp">apply()</code></p>
</th>
<th style="width:50%">
<p>Using <code class="language-cpp">index_sequence</code></p>
</th>
</tr>
<tr>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">template &lt;class P, class Q&gt;
auto dot_product(P p, Q q) {
    return std::apply([&amp;](auto... p_elems){
        return std::apply([&amp;](auto... q_elems){
            return (... + (p_elems * q_elems));
        }, q)
    }, p);
}</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">template &lt;size_t... Is, class P, class Q&gt;
auto dot_product(std::index_sequence&lt;Is...&gt;, P p, Q, q) {
    return (... + (std::get&lt;Is&gt;(p) * std::get&lt;Is&gt;(q)));
}

template &lt;class P, class Q&gt;
auto dot_product(P p, Q q) {
    return dot_product(
        std::make_index_sequence&lt;std::tuple_size&lt;P&gt;::value&gt;{},
        p, q);
}</code></pre>
</td>
</tr>
</table>

<p>Regardless of which option you dislike the least, both are limited to only <code class="language-cpp">std::tuple</code>s. We don't have the ability to do this at all for any of the other kinds of types that can be used in a <a href="https://wg21.link/p0144">structured binding declaration</a> - because we need to explicit list the correct number of identifiers, and we might not know how many there are.</p>
<h2 id="proposal">2. Proposal</h2>
<p>We propose to extend the structured bindings syntax to allow the user to introduce a pack as the last identifier, following the usual rules of pack declarations (must be trailing, and packs are introduced with leading <code class="language-cpp">...</code>):</p>
<pre class="codehilite"><code class="language-cpp">std::tuple&lt;X, Y, Z&gt; f();
auto [x,y,z] = f();          // OK today
auto [...xs] = f();          // proposed: xs is a pack of length three containing an X, Y, and a Z
auto [x, ...rest] = f();     // proposed: x is an X, rest... is a pack of length two
auto [x,y,z, ...rest] = f(); // proposed: rest... is an empty pack
auto [x, ...rest, z] = f();  // ill-formed: non-trailing pack. This is for consistency with, for instance, function parameter packs</code></pre>


<p>If we additionally add the structured binding customization machinery to <code class="language-cpp">std::integer_sequence</code>, this could greatly simplify generic code:</p>
<table style="width:100%">
<tr><th></th><th>Today</th><th>Proposed</th></tr>
<tr>
<td>
<p>Implementing<br/><code class="language-cpp">std::apply()</code></p>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">namespace detail {
    template &lt;class F, class Tuple, std::size_t... I&gt;
    constexpr decltype(auto) apply_impl(F &amp;&amp;f, Tuple &amp;&amp;t,
        std::index_sequence&lt;I...&gt;) 
    {
        return std::invoke(std::forward&lt;F&gt;(f),
            std::get&lt;I&gt;(std::forward&lt;Tuple&gt;(t))...);
    }
}

template &lt;class F, class Tuple&gt;
constexpr decltype(auto) apply(F &amp;&amp;f, Tuple &amp;&amp;t) 
{
    return detail::apply_impl(
        std::forward&lt;F&gt;(f), std::forward&lt;Tuple&gt;(t),
        std::make_index_sequence&lt;std::tuple_size_v&lt;
            std::decay_t&lt;Tuple&gt;&gt;&gt;{});
}</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">template &lt;class F, class Tuple&gt;
constexpr decltype(auto) apply(F &amp;&amp;f, Tuple &amp;&amp;t)
{
    auto&amp;&amp; [...elems] = t;
    return std::invoke(std::forward&lt;F&gt;(f),
        forward_like&lt;Tuple, decltype(elems)&gt;(elems)...);
}</code></pre>
</td>
</tr>
<tr>
<td>
<p><code class="language-cpp">dot_product()</code>,<br/>nested</p>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">template &lt;class P, class Q&gt;
auto dot_product(P p, Q q) {
    return std::apply([&amp;](auto... p_elems){
        return std::apply([&amp;](auto... q_elems){
            return (... + (p_elems * q_elems));
        }, q)
    }, p);
}</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">template &lt;class P, class Q&gt;
auto dot_product(P p, Q q) {
    // no indirection!
    auto&amp;&amp; [...p_elems] = p;
    auto&amp;&amp; [...q_elems] = q;
    return (... + (p_elems * q_elems));
}</code></pre>
</td></tr>
<tr>
<td>
<p><code class="language-cpp">dot_product()</code>,</br><code class="language-cpp">index_sequence</code></p>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">template &lt;size_t... Is, class P, class Q&gt;
auto dot_product(std::index_sequence&lt;Is...&gt;, P p, Q, q) {
    return (... + (std::get&lt;Is&gt;(p) * std::get&lt;Is&gt;(q)));
}

template &lt;class P, class Q&gt;
auto dot_product(P p, Q q) {
    return dot_product(
        std::make_index_sequence&lt;std::tuple_size_v&lt;P&gt;&gt;{},
        p, q);
}</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">template &lt;class P, class Q&gt;
auto dot_product(P p, Q q) {
    // no helper function necessary!
    auto [...Is] = std::make_index_sequence&lt;
        std::tuple_size_v&lt;P&gt;&gt;{};
    return (... + (std::get&lt;Is&gt;(p) * std::get&lt;Is&gt;(q)));
}</code></pre>
</td></tr>
</table>

<p>Not only are these implementations more concise, but they are also more functional. I can just as easily use <code class="language-cpp">apply()</code> with user-defined types as I can with <code class="language-cpp">std::tuple</code>:</p>
<pre class="codehilite"><code class="language-cpp">struct Point {
    int x, y, z;
};

Point getPoint();
double calc(int, int, int);

double result = std::apply(calc, getPoint()); // ill-formed today, ok with proposed implementation</code></pre>


<h2 id="wording">3. Wording</h2>
<p>Add a new grammar option for <em>simple-declaration</em> to 10 [dcl.dcl]:</p>
<blockquote>
<i>simple-declaration:</i><br />
<div style="margin-left: 2em;"><i>decl-specifier-seq</i> <i>init-declarator-list<sub>opt</sub></i> ;<br /></div>
<div style="margin-left: 2em;"><i>attribute-specifier-seq</i> <i>decl-specifier-seq</i> <i>init-declarator-list</i> ;</div>
<div style="margin-left: 2em;"><i>attribute-specifier-seq<sub>opt</sub></i> <i>decl-specifier-seq</i> <i>ref-qualifier<sub>opt</sub></i> [ <i>identifier-list</i> ] <i>initializer</i> ;</div>
<div style="margin-left: 2em;"><ins><i>attribute-specifier-seq<sub>opt</sub></i> <i>decl-specifier-seq</i> <i>ref-qualifier<sub>opt</sub></i> [ <i>identifier-list</i>,<sub>opt</sub> ...<i>identifier</i> ] <i>initializer</i> ;</ins></div>
</blockquote>

<p>Expand 10 [dcl.dcl] paragraph 8:</p>
<blockquote>
<p>A <em>simple-declaration</em> with an <em>identifier-list</em> <ins>or an <em>identifier</em> with preceding ellipsis</ins> is called a structured binding declaration ([dcl.struct.bind]). The <em>decl-specifier-seq</em> shall contain only the <em>type-specifier</em> <code class="language-cpp">auto</code> and <em>cv-qualifiers</em>. The <em>initializer</em> shall be of the form "= <em>assignment-expression</em>", of the form "{ <em>assignment-expression</em> }", or of the form "( <em>assignment-expression</em> )", where the <em>assignment-expression</em> is of array or non-union class type.</p>
</blockquote>
<p>Change 11.5 [dcl.struct.bind] paragraph 1:</p>
<blockquote>
A structured binding declaration introduces <del>the identifiers v<sub>0</sub>, v<sub>1</sub>, v<sub>2</sub>, ... of the <i>identifier-list</i> as</del> names ([basic.scope.declarative]) of <i>structured bindings</i>. <ins>If the declaration contains an <i>identifier-list</i>, the declaration introduces the identifiers v<sub>0</sub>, v<sub>1</sub>, v<sub>2</sub>, ... of the <i>identifier-list</i> as names. If the declaration contains an <i>identifier</i> with preceding ellipsis, the declaration introduces a <i>structured binding pack</i> ([temp.variadic]).</ins> Let <i>cv</i> denote the <i>cv-qualifiers</i> in the <i>decl-specifier-seq</i>. 
</blockquote>

<p>Introduce a new paragraph after [dcl.struct.bind] paragraph 1, introducing the term "structured binding size":</p>
<blockquote>
<ins>The <i>structured binding size</i> of a type <code>E</code> is the required number of names that need to be introduced by the structured binding declaration, as defined below. If there is no structured binding pack, then the number of elements in the <i>identifier-list</i> shall be equal to the structured binding size. Otherwise, the number of elements of the structured binding pack is the structured binding size less the number of elements in the <i>identifier-list</i>.
</blockquote>

<p>Change [dcl.struct.bind] paragraph 2 to define a structured binding size:</p>
<blockquote>
If <code>E</code> is an array type with element type <code>T</code>, <del>the number of elements in the <i>identifier-list</i></del> <ins>the structured binding size of <code>E</code></ins> shall be equal to the number of elements of <code>E</code>. <del>Each <i>v<sub>i</sub></i></del> <ins>The <i>i</i><sup>th</sup> <i>identifier</i></ins> is the name of an lvalue that refers to the element <i>i</i> of the array and whose type is <code>T</code>; the referenced type is <code>T</code>.
</blockquote>

<p>Change [dcl.struct.bind] paragraph 3 to define a structured binding size:</p>
<blockquote>
Otherwise, if the <i>qualified-id</i> <code>std::tuple_size&lt;E></code> names a complete type, the expression <code class="language-cpp">std::tuple_size&lt;E>::value</code> shall be a well-formed integral constant expression and the <del>number of elements in the <i>identifier-list</i></del> <ins>structured binding size of <code>E</code></ins> shall be equal to the value of that expression. [...] <del>Each <i>v<sub>i</sub></i></del> <ins>The <i>i</i><sup>th</sup> <i>identifier</i></ins> is the name of an lvalue of type <code class="">T<sub>i</sub></code> that refers to the object bound to <code class="">r<sub>i</sub></code>; the referenced type is <code class="">T<sub>i</sub></code>.
</blockquote>

<p>Change [dcl.struct.bind] paragraph 4 to define a structured binding size:</p>
<blockquote>
Otherwise, all of <code>E</code>'s non-static data members shall be direct members of <code>E</code> or of the same base class of <code>E</code>, well-formed when named as <code>e.name</code> in the context of the structured binding, <code>E</code> shall not have an anonymous union member, and the <del>number of elements in the <i>identifier-list</i></del> <ins>structured binding size of <code>E</code></ins> shall be equal to the number of non-static data members of <code>E</code>. Designating the non-static data members of <code>E</code> as <code class="">m<sub>0</sub>, m<sub>1</sub>, m<sub>2</sub>, . . .</code> (in declaration order), <del>each <code class="">v<sub>i</i></code></del> <ins>the <i>i</i><sup>th</sup> <i>identifier</i></ins> is the name of an lvalue that refers to the member <code class="">m<sub>i</sub></code> of <code>e</code> and whose type is <i>cv</i> <code class="">T<sub>i</sub></code>, where <code class="">T<sub>i</sub></code> is the declared type of that member; the referenced type is <i>cv</i> <code class="">T<sub>i</sub></code>. The lvalue is a bit-field if that member is a bit-field.
</blockquote>

<p>Add a new clause to 17.6.3 [temp.variadic], after paragraph 3:</p>
<blockquote>
<ins>A <i>structured binding pack</i> is an <i>identifier</i> that introduces zero or more <i>structured binding</i>s ([dcl.struct.bind]). <i>[ Example</i>

```
auto foo() -> int(&)[2];
auto [...a] = foo();          // a is a structured binding pack containing 2 elements
auto [b, c, ...d] = foo();    // d is a structured binding pack containing 0 elements
auto [e, f, g, ...h] = foo(); // error: too many identifiers
```

<ins><i>-end example]</i></ins></blockquote>

<p>In 17.6.3 [temp.variadic], change paragraph 4:</p>
<blockquote>
<p>A <em>pack</em> is a template parameter pack, a function parameter pack, <del>or</del> an <em>init-capture</em> pack<ins>, or a structured binding pack</ins>. The number of elements of a template parameter pack or a function parameter pack is the number of arguments provided for the parameter pack. The number of elements of an <em>init-capture</em> pack is the number of elements in the pack expansion of its <em>initializer</em>.</p>
</blockquote>
<p>In 17.6.3 [temp.variadic], paragraph 5 (describing pack expansions) remains unchanged.</p>
<p>In 17.6.3 [temp.variadic], add a bullet to paragraph 8:</p>
<blockquote>
Such an element, in the context of the instantiation, is interpreted as follows:
<ul>
<li> if the pack is a template parameter pack, the element is a template parameter ([temp.param]) of the corresponding kind (type or non-type) designating the 
<i>i</i><sup>th</sup> corresponding type or value template argument;
<li> if the pack is a function parameter pack, the element is an <i>id-expression</i> designating the  <i>i</i><sup>th</sup> function parameter that resulted from instantiation of the function parameter pack declaration; otherwise
<li> if the pack is an <i>init-capture</i> pack, the element is an <i>id-expression</i> designating the variable introduced by the <i>i</i><sup>th</sup>th <i>init-capture</i> that resulted from instantiation of the <i>init-capture</i> pack<del>.</del> <ins>; otherwise</ins>
<li><ins> if the pack is a structured binding pack, the element is an <i>id-expression</i> designating the <i>i</i><sup>th</sup> structured binding that resulted from the structured binding declaration.</ins>
</ul>
</blockquote>

<h2 id="acknowledgements">4. Acknowledgements</h2>
<p>Thanks to Michael Park and Tomasz Kamiński for their helpful feedback.</p>
<h2 id="references">5. References</h2><ul><li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3915.pdf">[N3915]</a><span style="margin-left: 5px;">"apply() call a function with arguments from a tuple (V3)" by Peter Sommerlad, 2014-02-14</span></li><li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0144r2.pdf">[P0144]</a><span style="margin-left: 5px;">"Structured Bindings" by Herb Sutter, 2016-03-16</span></li></ul>
</html>