<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Library utilities for <=></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
}
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-size: 83%;
}
a.self-link:hover {
    opacity: 1;
}
a.self-link::before {
    content: "§";
}</style>
<style type="text/css">/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+c+cpp&plugins=line-highlight */
/**
 * 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;
	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;
}

: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,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
	color: #9a6e3a;
}

.token.atrule,
.token.attr-value,
.token.keyword {
	color: #07a;
}

.token.function,
.token.class-name {
	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;
}

pre[data-line] {
	position: relative;
	padding: 1em 0 1em 3em;
}

.line-highlight {
	position: absolute;
	left: 0;
	right: 0;
	padding: inherit 0;
	margin-top: 1em; /* Same as .prism’s padding-top */

	background: hsla(24, 20%, 50%,.08);
	background: linear-gradient(to right, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));

	pointer-events: none;

	line-height: inherit;
	white-space: pre;
}

	.line-highlight:before,
	.line-highlight[data-end]:after {
		content: attr(data-start);
		position: absolute;
		top: .4em;
		left: .6em;
		min-width: 1em;
		padding: 0 .5em;
		background-color: hsla(24, 20%, 50%,.4);
		color: hsl(24, 20%, 95%);
		font: bold 65%/1.5 sans-serif;
		text-align: center;
		vertical-align: .3em;
		border-radius: 999px;
		text-shadow: none;
		box-shadow: 0 1px white;
	}

	.line-highlight[data-end]:after {
		content: attr(data-end);
		top: auto;
		bottom: .4em;
	}

.line-numbers .line-highlight:before,
.line-numbers .line-highlight:after {
	content: none;
}

</style>
<script type="text/javascript">/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+c+cpp+nasm+rust&plugins=line-highlight */
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={manual:_self.Prism&&_self.Prism.manual,disableWorkerMessageHandler:_self.Prism&&_self.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof r?new r(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,t){var r=n.util.type(e);switch(t=t||{},r){case"Object":if(t[n.util.objId(e)])return t[n.util.objId(e)];var a={};t[n.util.objId(e)]=a;for(var l in e)e.hasOwnProperty(l)&&(a[l]=n.util.clone(e[l],t));return a;case"Array":if(t[n.util.objId(e)])return t[n.util.objId(e)];var a=[];return t[n.util.objId(e)]=a,e.forEach(function(e,r){a[r]=n.util.clone(e,t)}),a}return e}},languages:{extend:function(e,t){var r=n.util.clone(n.languages[e]);for(var a in t)r[a]=t[a];return r},insertBefore:function(e,t,r,a){a=a||n.languages;var l=a[e];if(2==arguments.length){r=arguments[1];for(var i in r)r.hasOwnProperty(i)&&(l[i]=r[i]);return l}var o={};for(var s in l)if(l.hasOwnProperty(s)){if(s==t)for(var i in r)r.hasOwnProperty(i)&&(o[i]=r[i]);o[s]=l[s]}var u=a[e];return a[e]=o,n.languages.DFS(n.languages,function(t,n){n===u&&t!=e&&(this[t]=o)}),o},DFS:function(e,t,r,a){a=a||{};for(var l in e)e.hasOwnProperty(l)&&(t.call(e,l,e[l],r||l),"Object"!==n.util.type(e[l])||a[n.util.objId(e[l])]?"Array"!==n.util.type(e[l])||a[n.util.objId(e[l])]||(a[n.util.objId(e[l])]=!0,n.languages.DFS(e[l],t,l,a)):(a[n.util.objId(e[l])]=!0,n.languages.DFS(e[l],t,null,a)))}},plugins:{},highlightAll:function(e,t){n.highlightAllUnder(document,e,t)},highlightAllUnder:function(e,t,r){var a={callback:r,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};n.hooks.run("before-highlightall",a);for(var l,i=a.elements||e.querySelectorAll(a.selector),o=0;l=i[o++];)n.highlightElement(l,t===!0,a.callback)},highlightElement:function(t,r,a){for(var l,i,o=t;o&&!e.test(o.className);)o=o.parentNode;o&&(l=(o.className.match(e)||[,""])[1].toLowerCase(),i=n.languages[l]),t.className=t.className.replace(e,"").replace(/\s+/g," ")+" language-"+l,t.parentNode&&(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(n.hooks.run("before-sanity-check",u),!u.code||!u.grammar)return u.code&&(n.hooks.run("before-highlight",u),u.element.textContent=u.code,n.hooks.run("after-highlight",u)),n.hooks.run("complete",u),void 0;if(n.hooks.run("before-highlight",u),r&&_self.Worker){var g=new Worker(n.filename);g.onmessage=function(e){u.highlightedCode=e.data,n.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,a&&a.call(u.element),n.hooks.run("after-highlight",u),n.hooks.run("complete",u)},g.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,a&&a.call(t),n.hooks.run("after-highlight",u),n.hooks.run("complete",u)},highlight:function(e,t,a){var l={code:e,grammar:t,language:a};return n.hooks.run("before-tokenize",l),l.tokens=n.tokenize(l.code,l.grammar),n.hooks.run("after-tokenize",l),r.stringify(n.util.encode(l.tokens),l.language)},matchGrammar:function(e,t,r,a,l,i,o){var s=n.Token;for(var u in r)if(r.hasOwnProperty(u)&&r[u]){if(u==o)return;var g=r[u];g="Array"===n.util.type(g)?g:[g];for(var c=0;c<g.length;++c){var h=g[c],f=h.inside,d=!!h.lookbehind,m=!!h.greedy,p=0,y=h.alias;if(m&&!h.pattern.global){var v=h.pattern.toString().match(/[imuy]*$/)[0];h.pattern=RegExp(h.pattern.source,v+"g")}h=h.pattern||h;for(var b=a,k=l;b<t.length;k+=t[b].length,++b){var w=t[b];if(t.length>e.length)return;if(!(w instanceof s)){if(m&&b!=t.length-1){h.lastIndex=k;var _=h.exec(e);if(!_)break;for(var j=_.index+(d?_[1].length:0),P=_.index+_[0].length,A=b,x=k,O=t.length;O>A&&(P>x||!t[A].type&&!t[A-1].greedy);++A)x+=t[A].length,j>=x&&(++b,k=x);if(t[b]instanceof s)continue;I=A-b,w=e.slice(k,x),_.index-=k}else{h.lastIndex=0;var _=h.exec(w),I=1}if(_){d&&(p=_[1]?_[1].length:0);var j=_.index+p,_=_[0].slice(p),P=j+_.length,N=w.slice(0,j),S=w.slice(P),C=[b,I];N&&(++b,k+=N.length,C.push(N));var E=new s(u,f?n.tokenize(_,f):_,y,_,m);if(C.push(E),S&&C.push(S),Array.prototype.splice.apply(t,C),1!=I&&n.matchGrammar(e,t,r,b,k,!0,u),i)break}else if(i)break}}}}},tokenize:function(e,t){var r=[e],a=t.rest;if(a){for(var l in a)t[l]=a[l];delete t.rest}return n.matchGrammar(e,r,t,0,0,!1),r},hooks:{all:{},add:function(e,t){var r=n.hooks.all;r[e]=r[e]||[],r[e].push(t)},run:function(e,t){var r=n.hooks.all[e];if(r&&r.length)for(var a,l=0;a=r[l++];)a(t)}}},r=n.Token=function(e,t,n,r,a){this.type=e,this.content=t,this.alias=n,this.length=0|(r||"").length,this.greedy=!!a};if(r.stringify=function(e,t,a){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return r.stringify(n,t,e)}).join("");var l={type:e.type,content:r.stringify(e.content,t,a),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:a};if(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=Object.keys(l.attributes).map(function(e){return e+'="'+(l.attributes[e]||"").replace(/"/g,"&quot;")+'"'}).join(" ");return"<"+l.tag+' class="'+l.classes.join(" ")+'"'+(o?" "+o:"")+">"+l.content+"</"+l.tag+">"},!_self.document)return _self.addEventListener?(n.disableWorkerMessageHandler||_self.addEventListener("message",function(e){var t=JSON.parse(e.data),r=t.language,a=t.code,l=t.immediateClose;_self.postMessage(n.highlight(a,n.languages[r],r)),l&&_self.close()},!1),_self.Prism):_self.Prism;var a=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return a&&(n.filename=a.src,n.manual||a.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):document.addEventListener("DOMContentLoaded",n.highlightAll))),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
Prism.languages.markup={comment:/<!--[\s\S]*?-->/,prolog:/<\?[\s\S]+?\?>/,doctype:/<!DOCTYPE[\s\S]+?>/i,cdata:/<!\[CDATA\[[\s\S]*?]]>/i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/(^|[^\\])["']/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&amp;/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup;
Prism.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(?:;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^{}\s][^{};]*?(?=\s*\{)/,string:{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.languages.css,Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/(<style[\s\S]*?>)[\s\S]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css",greedy:!0}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag));
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!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+\())[\w.\\]+/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":/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/};
Prism.languages.c=Prism.languages.extend("clike",{keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|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:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*\/%&|^!=<>]=?/,number:/(?:\b0x[\da-f]+|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?)[ful]*/i}),Prism.languages.insertBefore("c","string",{macro:{pattern:/(^\s*)#\s*[a-z]+(?:[^\r\n\\]|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,alias:"property",inside:{string:{pattern:/(#\s*include\s*)(?:<.+?>|("|')(?:\\?.)+?\2)/,lookbehind:!0},directive:{pattern:/(#\s*)\b(?:define|defined|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|SEEK_CUR|SEEK_END|SEEK_SET|stdin|stdout|stderr)\b/}),delete Prism.languages.c["class-name"],delete Prism.languages.c["boolean"];
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|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|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|[?:~]|[-+*\/%&|^!=<>]=?|\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+)\w+/i,lookbehind:!0}}),Prism.languages.insertBefore("cpp","string",{"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}});
Prism.languages.nasm={comment:/;.*$/m,string:/(["'`])(?:\\.|(?!\1)[^\\\r\n])*\1/,label:{pattern:/(^\s*)[A-Za-z._?$][\w.?$@~#]*:/m,lookbehind:!0,alias:"function"},keyword:[/\[?BITS (?:16|32|64)\]?/,{pattern:/(^\s*)section\s*[a-zA-Z.]+:?/im,lookbehind:!0},/(?:extern|global)[^;\r\n]*/i,/(?:CPU|FLOAT|DEFAULT).*$/m],register:{pattern:/\b(?:st\d|[xyz]mm\d\d?|[cdt]r\d|r\d\d?[bwd]?|[er]?[abcd]x|[abcd][hl]|[er]?(?:bp|sp|si|di)|[cdefgs]s)\b/i,alias:"variable"},number:/(?:\b|(?=\$))(?:0[hx][\da-f]*\.?[\da-f]+(?:p[+-]?\d+)?|\d[\da-f]+[hx]|\$\d[\da-f]*|0[oq][0-7]+|[0-7]+[oq]|0[by][01]+|[01]+[by]|0[dt]\d+|\d*\.?\d+(?:\.?e[+-]?\d+)?[dt]?)\b/i,operator:/[\[\]*+\-\/%<>=&|$!]/};
Prism.languages.rust={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:[{pattern:/b?r(#*)"(?:\\.|(?!"\1)[^\\\r\n])*"\1/,greedy:!0},{pattern:/b?"(?:\\.|[^\\\r\n"])*"/,greedy:!0}],"char":{pattern:/b?'(?:\\(?:x[0-7][\da-fA-F]|u{(?:[\da-fA-F]_*){1,6}|.)|[^\\\r\n\t'])'/,alias:"string"},"lifetime-annotation":{pattern:/'[^\s>']+/,alias:"symbol"},keyword:/\b(?:abstract|alignof|as|be|box|break|const|continue|crate|do|else|enum|extern|false|final|fn|for|if|impl|in|let|loop|match|mod|move|mut|offsetof|once|override|priv|pub|pure|ref|return|sizeof|static|self|struct|super|true|trait|type|typeof|unsafe|unsized|use|virtual|where|while|yield)\b/,attribute:{pattern:/#!?\[.+?\]/,greedy:!0,alias:"attr-name"},"function":[/\w+(?=\s*\()/,/\w+!(?=\s*\(|\[)/],"macro-rules":{pattern:/\w+!/,alias:"function"},number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(\d(?:_?\d)*)?\.?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)(?:_?(?:[iu](?:8|16|32|64)?|f32|f64))?\b/,"closure-params":{pattern:/\|[^|]*\|(?=\s*[{-])/,inside:{punctuation:/[|:,]/,operator:/[&*]/}},punctuation:/[{}[\];(),:]|\.+|->/,operator:/[-+*\/%!^]=?|=[=>]?|@|&[&=]?|\|[|=]?|<<?=?|>>?=?/};
!function(){function e(e,t){return Array.prototype.slice.call((t||document).querySelectorAll(e))}function t(e,t){return t=" "+t+" ",(" "+e.className+" ").replace(/[\n\t]/g," ").indexOf(t)>-1}function n(e,n,i){n="string"==typeof n?n:e.getAttribute("data-line");for(var o,l=n.replace(/\s+/g,"").split(","),a=+e.getAttribute("data-line-offset")||0,s=r()?parseInt:parseFloat,d=s(getComputedStyle(e).lineHeight),u=t(e,"line-numbers"),c=0;o=l[c++];){var p=o.split("-"),m=+p[0],f=+p[1]||m,h=e.querySelector('.line-highlight[data-range="'+o+'"]')||document.createElement("div");if(h.setAttribute("aria-hidden","true"),h.setAttribute("data-range",o),h.className=(i||"")+" line-highlight",u&&Prism.plugins.lineNumbers){var g=Prism.plugins.lineNumbers.getLine(e,m),y=Prism.plugins.lineNumbers.getLine(e,f);g&&(h.style.top=g.offsetTop+"px"),y&&(h.style.height=y.offsetTop-g.offsetTop+y.offsetHeight+"px")}else h.setAttribute("data-start",m),f>m&&h.setAttribute("data-end",f),h.style.top=(m-a-1)*d+"px",h.textContent=new Array(f-m+2).join(" \n");u?e.appendChild(h):(e.querySelector("code")||e).appendChild(h)}}function i(){var t=location.hash.slice(1);e(".temporary.line-highlight").forEach(function(e){e.parentNode.removeChild(e)});var i=(t.match(/\.([\d,-]+)$/)||[,""])[1];if(i&&!document.getElementById(t)){var r=t.slice(0,t.lastIndexOf(".")),o=document.getElementById(r);o&&(o.hasAttribute("data-line")||o.setAttribute("data-line",""),n(o,i,"temporary "),document.querySelector(".temporary.line-highlight").scrollIntoView())}}if("undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector){var r=function(){var e;return function(){if("undefined"==typeof e){var t=document.createElement("div");t.style.fontSize="13px",t.style.lineHeight="1.5",t.style.padding=0,t.style.border=0,t.innerHTML="&nbsp;<br />&nbsp;",document.body.appendChild(t),e=38===t.offsetHeight,document.body.removeChild(t)}return e}}(),o=0;Prism.hooks.add("before-sanity-check",function(t){var n=t.element.parentNode,i=n&&n.getAttribute("data-line");if(n&&i&&/pre/i.test(n.nodeName)){var r=0;e(".line-highlight",n).forEach(function(e){r+=e.textContent.length,e.parentNode.removeChild(e)}),r&&/^( \n)+$/.test(t.code.slice(-r))&&(t.code=t.code.slice(0,-r))}}),Prism.hooks.add("complete",function l(e){var r=e.element.parentNode,a=r&&r.getAttribute("data-line");if(r&&a&&/pre/i.test(r.nodeName)){clearTimeout(o);var s=Prism.plugins.lineNumbers,d=e.plugins&&e.plugins.lineNumbers;t(r,"line-numbers")&&s&&!d?Prism.hooks.add("line-numbers",l):(n(r,a),o=setTimeout(i,1))}}),window.addEventListener("hashchange",i),window.addEventListener("resize",function(){var e=document.querySelectorAll("pre[data-line]");Array.prototype.forEach.call(e,function(e){n(e)})})}}();
</script>

</head>
<body>
<address align=right>
Document Number: P1188R0 <br />
Date: 2019-01-22 <br />
Audience: LEWG <br />
Reply-To: Barry Revzin, barry dot revzin at gmail dot com <br />
</address>
<hr /><h1 align=center><p>Library utilities for <code class="language-cpp">&lt;=&gt;</code></p></h1>
<h2>Contents</h2>
<div class="toc">
<ol>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#motivations-and-proposals">Motivations and Proposals</a><ol>
<li><a href="#add-a-type-trait-stdcompare_3way_type">Add a type trait: std::compare_3way_type</a></li>
<li><a href="#add-a-pair-of-concepts-threewaycomparable-and-threewaycomparablewith">Add a pair of concepts: ThreeWayComparable and ThreeWayComparableWith</a></li>
<li><a href="#replace-stdcompare_3way">Replace std::compare_3way()</a></li>
</ol>
</li>
<li><a href="#wording">Wording</a><ol>
<li><a href="#alternate-wording-for-stdcompare_3way">Alternate Wording for std::compare_3way</a></li>
</ol>
</li>
<li><a href="#acknowledgments">Acknowledgments</a></li>
<li><a href="#references">References</a></li>
</ol>
</div>

<h2 id="introduction">1. Introduction<a class="self-link" href="#introduction"></a></h2>
<p>In San Diego, I brought <a href="https://wg21.link/p1186r0" title="When do you actually use &lt;=&gt;?">P1186R0</a> and <a href="https://wg21.link/p1187r0" title="A type trait for std::compare_3way()'s type">P1187R0</a> to LEWG which did the following:</p>
<ul>
<li>Removed <code class="language-cpp">std::compare_3way()</code>, the algorithm, and replaced it with a function object that invokes <code class="language-cpp">&lt;=&gt;</code> on its operands</li>
<li>Added a type trait, <code class="language-cpp">std::compare_3way_type</code>, for the type of <code class="language-cpp">&lt;=&gt;</code></li>
</ul>
<p>The motivation for removing <code class="language-cpp">std::compare_3way()</code> was driven by P1186R0 redefining <code class="language-cpp">&lt;=&gt;</code> in such a way that the algorithm itself would be completely pointless - <code class="language-cpp">std::compare_3way(a, b)</code> would have become a long way of writing <code class="language-cpp">a &lt;=&gt; b</code>. However, P1186 is changing course in a way that does not make the algorithm obsolete.</p>
<p>Since San Diego, I've also discovered the need for a new fundamental library tool for implementing <code class="language-cpp">&lt;=&gt;</code>: we need a <code class="language-cpp">concept</code>. This concept is very important, as I'll illustrate here. </p>
<p>To try to avoid confusion, instead of writing multiple R1s that refer to each other in a complex maze of indirections, I thought I would simply write one single paper that includes within it all the changes I am proposing for the Library for dealing with <code class="language-cpp">&lt;=&gt;</code>. This is that paper.</p>
<h2 id="motivations-and-proposals">2. Motivations and Proposals<a class="self-link" href="#motivations-and-proposals"></a></h2>
<p>This paper proposes two additions and one replacement to the library. I will go through them in increasing order of complexity.</p>
<h3 id="add-a-type-trait-stdcompare_3way_type">2.1. Add a type trait: <code class="language-cpp">std::compare_3way_type</code><a class="self-link" href="#add-a-type-trait-stdcompare_3way_type"></a></h3>
<p>For some types, in order to implement <code class="language-cpp">operator&lt;=&gt;()</code> you have to defer to the implementation of the 
comparison operators of other types. And more than that, you need to know what the comparison category is for those types you defer to in order to know what comparison category to return. For example, to implement <code class="language-cpp">&lt;=&gt;</code> for a type like <code class="language-cpp">optional&lt;T&gt;</code>:</p>
<pre class="codehilite"><code class="language-cpp">template &lt;typename T, typename U&gt;
constexpr ??? operator&lt;=&gt;(optional&lt;T&gt; const&amp; lhs, optional&lt;U&gt; const&amp; rhs) {
    if (lhs &amp;&amp; rhs) {
        return *lhs &lt;=&gt; *rhs;
    } else {
        return lhs.has_value() &lt;=&gt; rhs.has_value();
    }
}</code></pre>


<p>What do we put in the <code class="language-cpp">???</code>? We can't put <code class="language-cpp">auto</code>, because our two <code class="language-cpp">return</code> statements might have different types; the <code class="language-cpp">bool</code> comparison has type <code class="language-cpp">strong_ordering</code> but the other one could have any of the five comparisons. Whichever comparison category <code class="language-cpp">*lhs &lt;=&gt; *rhs</code> has, <code class="language-cpp">strong_ordering</code> will be convertible to it, so we can simply use that expression directly:</p>
<pre class="codehilite"><code class="language-cpp">template &lt;typename T, typename U&gt;
constexpr auto operator&lt;=&gt;(optional&lt;T&gt; const&amp; lhs, optional&lt;U&gt; const&amp; rhs)
    -&gt; decltype(*lhs &lt;=&gt; *rhs)
{ /* ... */ }</code></pre>


<p>This will come up basically every time you need to defer to a template parameter:</p>
<pre class="codehilite"><code class="language-cpp">template &lt;typename T, typename U&gt;
auto operator&lt;=&gt;(vector&lt;T&gt; const&amp; lhs, vector&lt;U&gt; const&amp; rhs)
    -&gt; decltype(lhs[0] &lt;=&gt; rhs[0]);

template &lt;typename T1, typename E1, typename T2, typename E2&gt;
auto operator&lt;=&gt;(expected&lt;T1,E1&gt; const&amp; lhs, expected&lt;T2,E2&gt; const&amp; rhs)
    -&gt; common_comparison_category_t&lt;
            decltype(lhs.value() &lt;=&gt; rhs.value()),
            decltype(lhs.error() &lt;=&gt; rhs.error())&gt;;</code></pre>


<p>We see the same thing over and over and over again. We need to know what comparison category to return, and this comparison category is a property of the types that we're comparing. </p>
<p>That's a type trait. A type trait that's currently missing from the standard library:</p>
<pre class="codehilite"><code class="language-cpp">template&lt;class T, class U = T&gt; struct compare_3way_type;

template&lt;class T, class U = T&gt;
using compare_3way_type_t = typename compare_3way_type&lt;T, U&gt;::type;</code></pre>


<p>Such that <code class="language-cpp">compare_3way_type&lt;T, U&gt;</code> has a member <code class="language-cpp">type</code> that is <code class="language-cpp">decltype(declval&lt;CREF&lt;T&gt;&gt;() &lt;=&gt; declval&lt;CREF&lt;U&gt;&gt;())</code> is that is a valid expression, and no member <code class="language-cpp">type</code> otherwise (where <code class="language-cpp">CREF&lt;T&gt;</code> is <code class="language-cpp">remove_reference_t&lt;T&gt; const&amp;</code>).</p>
<table style="width:100%">
<tr>
<th style="width:50%">
<p>Today</p>
</th>
<th style="width:50%">
<p>Proposed</p>
</th>
</tr>
<tr>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">template &lt;typename T, typename U&gt;
constexpr auto
operator&lt;=&gt;(optional&lt;T&gt; const&amp; lhs, optional&lt;U&gt; const&amp; rhs)
    -&gt; decltype(*lhs &lt;=&gt; *rhs);</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">template &lt;typename T, typename U&gt;
constexpr compare_3way_type_t&lt;T, U&gt;
operator&lt;=&gt;(optional&lt;T&gt; const&amp; lhs, optional&lt;U&gt; const&amp; rhs);</code></pre>
</td>
</tr>
</table>

<h3 id="add-a-pair-of-concepts-threewaycomparable-and-threewaycomparablewith">2.2. Add a pair of concepts: <code class="language-cpp">ThreeWayComparable</code> and <code class="language-cpp">ThreeWayComparableWith</code><a class="self-link" href="#add-a-pair-of-concepts-threewaycomparable-and-threewaycomparablewith"></a></h3>
<p>One important piece of functionality that libraries will need to implement is to <em>conditionally</em> provide <code class="language-cpp">&lt;=&gt;</code> on class templates based on whether certain types provide <code class="language-cpp">&lt;=&gt;</code>. For example. <code class="language-cpp">vector&lt;T&gt;</code> should expose an <code class="language-cpp">operator&lt;=&gt;</code> if and only if <code class="language-cpp">T</code> has an <code class="language-cpp">operator&lt;=&gt;</code>.</p>
<p>Coming up with the correct way to implement this is non-trivial - I've gone through several incorrect implementations of it between the time when I started writing P1186R0 and now. The key problem to be solved is to ensure that <code class="language-cpp">&lt;</code> invokes <code class="language-cpp">&lt;=&gt;</code> if at all possible. That is:</p>
<pre class="codehilite"><code class="language-cpp">struct A {
    bool operator&lt;(A const&amp;) const;
};

struct B {
    strong_ordering operator&lt;=&gt;(B const&amp;) const;
}

template &lt;typename T&gt; vector&lt;T&gt; get();

get&lt;A&gt;() &lt; get&lt;A&gt;(); // should call vector&lt;A&gt;::operator&lt;
get&lt;B&gt;() &lt; get&lt;B&gt;(); // should call vector&lt;B&gt;::operator&lt;=&gt;</code></pre>


<p>The best way to solve this problem is with the use of concepts. We need to make <code class="language-cpp">operator&lt;=&gt;</code> <em>more constrained</em> than <code class="language-cpp">operator&lt;</code> (which might mean carefully ensuring subsumption, or just adding a constraint at all). This implementation strategy makes it possible to provide a complete implementation of conditional <code class="language-cpp">&lt;=&gt;</code> for <code class="language-cpp">vector&lt;T&gt;</code> as follows:</p>
<pre class="codehilite"><code class="language-cpp">// uncontrained ==, != is not necessary after P1185
template &lt;typename T&gt; bool operator==(vector&lt;T&gt; const&amp;, vector&lt;T&gt; const&amp;);

// unconstrained &lt;,&gt;,&lt;=,&gt;=
template &lt;typename T&gt; bool operator&lt;(vector&lt;T&gt; const&amp;, vector&lt;T&gt; const&amp;);
template &lt;typename T&gt; bool operator&gt;(vector&lt;T&gt; const&amp;, vector&lt;T&gt; const&amp;);
template &lt;typename T&gt; bool operator&lt;=(vector&lt;T&gt; const&amp;, vector&lt;T&gt; const&amp;);
template &lt;typename T&gt; bool operator&gt;=(vector&lt;T&gt; const&amp;, vector&lt;T&gt; const&amp;);

template &lt;ThreeWayComparable T&gt;  // this constraint is critical
compare_3way_type_t&lt;T&gt;           // from §2.1
operator&lt;=&gt;(vector&lt;T&gt; const&amp;, vector&lt;T&gt; const&amp;);</code></pre>


<p>This works because in the expression <code class="language-cpp">get&lt;B&gt;() &lt; get&lt;B&gt;()</code>, both <code class="language-cpp">operator&lt;</code> and <code class="language-cpp">operator&lt;=&gt;</code> are candidates with the same conversion sequences. The relevant order of tiebreakers in [over.match.best] is:</p>
<p>1.6. Prefer non-template to template<br />
1.7. Prefer more specialized template<br />
1.8. Prefer more constrained template<br />
1.9. Prefer derived constructor to inherited base constructor<br />
1.10. Prefer non-rewritten-candidate (i.e. <code class="language-cpp">&lt;</code>) to rewritten candidate (i.e. <code class="language-cpp">&lt;=&gt;</code>)<br />
1.11. Prefer non-reversed rewritten candidate to reversed rewritten candidate  </p>
<p>Importantly, preferring the <em>more constrained</em> candidate is an earlier tiebreaker than preferring the direct relational operator to spaceship. This is the rule that ensures that we can prefer <code class="language-cpp">&lt;=&gt;</code> by just making that operator function more constrained than any of the relational operators (which is trivial if none of them are constrained, as above).</p>
<p>There are other ways of ensuring <code class="language-cpp">&lt;=&gt;</code> gets invoked over <code class="language-cpp">&lt;</code>, but I believe this to be the best way since it only requires properly declaring <code class="language-cpp">&lt;=&gt;</code>.</p>
<p>And this way requires a concept for <code class="language-cpp">&lt;=&gt;</code>. This kind of concept is fairly fundamental, and because of its wide application and its subtle complexity, this paper proposes it be included in the standard library. Otherwise, everyone will have to write their own slightly different, potentially incorrect version of it. Due to concept subsumption, there is especial value of having a common understanding of what <code class="language-cpp">ThreeWayComparable</code> means. </p>
<p>With help from Casey Carter, the implementation I'm proposing is (again using <code class="language-cpp">CREF&lt;T&gt;</code> as alias for <code class="language-cpp">remove_reference_t&lt;T&gt; const&amp;</code>):</p>
<pre class="codehilite"><code class="language-cpp">template &lt;typename T, typename Cat&gt;
  concept compares-as = // exposition only
    Same&lt;common_comparison_category_t&lt;T, Cat&gt;, Cat&gt;;

template &lt;typename T, typename Cat=std::weak_equality&gt;
  concept ThreeWayComparable = requires(CREF&lt;T&gt; a, CREF&lt;T&gt; b) {
    { a &lt;=&gt; b } -&gt; compares-as&lt;Cat&gt;;
  };

template &lt;typename T, typename U,
          typename Cat=std::weak_equality&gt;
  concept ThreeWayComparableWith = 
    ThreeWayComparable&lt;T, Cat&gt; &amp;&amp;
    ThreeWayComparable&lt;U, Cat&gt; &amp;&amp;
    CommonReference&lt;CREF&lt;T&gt;, CREF&lt;U&gt;&gt; &amp;&amp;
    ThreeWayComparable&lt;
      common_reference_t&lt;CREF&lt;T&gt;, CREF&lt;U&gt;&gt;,
      Cat&gt; &amp;&amp;
    requires(CREF&lt;T&gt; t, CREF&lt;U&gt; u) {
      { t &lt;=&gt; u } -&gt; compares-as&lt;Cat&gt;;
      { u &lt;=&gt; t } -&gt; compares-as&lt;Cat&gt;;
    };</code></pre>


<p>This definition follows the practice of <code class="language-cpp">EqualityComparable</code> and <code class="language-cpp">StrictTotallyOrdered</code> in that we're not just checking syntactic correctness - we're doing a bit more than that: we're also requiring that <code class="language-cpp">&lt;=&gt;</code> actually produces one of the comparison categories. </p>
<p>The defaulted <code class="language-cpp">Cat</code> parameter allows users to refine their constraints. <code class="language-cpp">ThreeWayComparable&lt;T&gt;</code> just requires that <code class="language-cpp">T</code> provide some meaningful <code class="language-cpp">&lt;=&gt;</code>, whereas <code class="language-cpp">ThreeWayComparable&lt;T, std::partial_ordering&gt;</code> requires that <code class="language-cpp">T</code> provide an ordering.</p>
<p>Note that <code class="language-cpp">ThreeWayComparable&lt;T, std::strong_ordering&gt;</code> does <em>not</em> subsume <code class="language-cpp">ThreeWayComparable&lt;T, std::weak_ordering&gt;</code> despite being logically stricter. </p>
<h3 id="replace-stdcompare_3way">2.3. Replace <code class="language-cpp">std::compare_3way()</code><a class="self-link" href="#replace-stdcompare_3way"></a></h3>
<p>While P1186R0 made <code class="language-cpp">std::compare_3way()</code> completely pointless, that is no longer strictly the case with P1186R1. Nevertheless, this paper proposes removing that algorithm.</p>
<p>It is very easy to misuse without thinking about it because it automatically gives you a strong ordering - which isn't apparent from the name. It's very easy to fall into the trap of thinking that this is just the correct algorithm to use whenever you want a three-way comparison, and it's really not. Lawrence Crowl in <a href="https://wg21.link/p1380r0" title="Ambiguity and Insecurities with Three-Way Comparison">P1380R0</a> argues that the comparisons it synthesizes should be <code class="language-cpp">weak_XXX</code> instead of <code class="language-cpp">strong_XXX</code>, but even then it still means the library has to make one singular choice for what the correct comparison category to synthesize is. Which is the right default? Is there a right default?</p>
<p>I think it would be strictly superior to use the <code class="language-cpp">compare_3way_fallback&lt;strong_ordering&gt;</code> function as described in P1186R1, which really makes clear what is going on. It's easy to implement on the back of the language changed proposed in that paper, and it allows for a more powerful algorithm that is also more correct than <code class="language-cpp">std::compare_3way()</code>. So let's remove <code class="language-cpp">std::compare_3way()</code>.</p>
<p>On the other hand, having a function object that just invokes <code class="language-cpp">&lt;=&gt;</code> would actually be useful, and <code class="language-cpp">compare_3way</code> seems like the best name for it.</p>
<p>The question is: what should <code class="language-cpp">compare_3way</code>, the function object that invokes <code class="language-cpp">&lt;=&gt;</code>, look like? The model we have is <code class="language-cpp">std::less</code>, where <code class="language-cpp">std::less&lt;T&gt;</code> compares two objects of type <code class="language-cpp">T</code> and <code class="language-cpp">std::less&lt;void&gt;</code> deduces its arguments. But we also recently acquired <code class="language-cpp">std::ranges::less</code>, which at the moment mirrors the <code class="language-cpp">std::less</code> version except additionally using constrains. But <a href="https://wg21.link/p1252r0" title="Ranges Design Cleanup">P1252</a> proposes to simplify to just be a single transparent comparison. </p>
<p>The simplest version of a spaceship comparison function object would be to take the P1252 version:</p>
<pre class="codehilite"><code class="language-cpp">namespace std {
  struct compare_3way {
    template&lt;class T, class U&gt;
      requires ThreeWayComparableWith&lt;T, U&gt; // §2.2
              || BUILTIN_PTR_3WAY(T, U)     // see [range.cmp] for inspiration
    constexpr auto operator()(T&amp;&amp; t, U&amp;&amp; u) const;

    using is_transparent = unspecified;
  };
}</code></pre>


<p>This is inconsistent with <code class="language-cpp">std::less</code>, since it's not a template of any kind and there's no "fixed-type" version. But it's also a lot simpler. The question for LEWG is do they prefer the above or if they want to fully copy <code class="language-cpp">std::less</code>, which as it stands in the working draft today would be:</p>
<pre class="codehilite"><code class="language-cpp">namespace std {
  template&lt;class T = void&gt;
  struct compare_3way {
    constexpr auto operator()(const T&amp;, const T&amp;) const;
  };

  template &lt;&gt; struct compare_3way&lt;void&gt; {
    template&lt;class T, class U&gt;
    constexpr auto operator()(T&amp;&amp; t, U&amp;&amp; u) const
      -&gt; decltype(std::forward&lt;T&gt;(t) &lt;=&gt; std::forward&lt;U&gt;(u));
  };

  namespace ranges {
    template&lt;class T = void&gt;
      requires ThreeWayComparable&lt;T&gt; || Same&lt;T, void&gt; || BUILTIN_PTR_3WAY(const T&amp;, const T&amp;)
    struct compare_3way {
      constexpr auto operator()(const T&amp; x, const T&amp; y) const;
    };

    template&lt;&gt; struct compare_3way&lt;void&gt; {
      template&lt;class T, class U&gt;
        requires ThreeWayComparableWith&lt;T, U&gt; || BUILTIN_PTR_3WAY(T, U)
      constexpr auto operator()(T&amp;&amp; t, U&amp;&amp; u) const;

      using is_transparent = unspecified;
    };
  }
}</code></pre>


<h2 id="wording">3. Wording<a class="self-link" href="#wording"></a></h2>
<p>Add the new trait, concept, and function object into the <code class="language-cpp">&lt;compare&gt;</code> synopsis in 16.11.1 [compare.syn]:</p>
<blockquote><pre><code>namespace std {
  [...]

  // [cmp.common], common comparison category type  
  template&lt;class... Ts&gt;
  struct common_comparison_category {
    using type = see below;
  };
  template&lt;class... Ts&gt;
    using common_comparison_category_t = typename common_comparison_category&lt;Ts...&gt;::type;  

  <ins>// [cmp.threewaycomparable], concept ThreeWayComparable
  <ins>template&lt;class T, class Cat = weak_equality&gt;</ins>
    <ins>concept ThreeWayComparable = <i>see below</i>;</ins>
  <ins>template&lt;class T, class U, class Cat = weak_equality&gt;</ins>
    <ins>concept ThreeWayComparableWith = <i>see below</i>;</ins>

  <ins>// [cmp.3way], compare_3way</ins>
  <ins>template&lt;class T, class U = T&gt; struct compare_3way_type;</ins>

  <ins>template&lt;class T, class U = T&gt;</ins>
  <ins>  using compare_3way_type_t = typename compare_3way_type&lt;T, U&gt;::type;</ins>

  <ins>template&lt;class T = void&gt; struct compare_3way;</ins>
  <ins>template&lt;&gt; struct compare_3way&lt;void&gt;;</ins>

  <ins>namespace ranges {</ins>
    <ins>// [???] concept-constrained comparisons</ins>
    <ins>template&lt;class T = void&gt;</ins>
    <ins>  requires <i>see below</i></ins>
    <ins>struct compare_3way;</ins>

    <ins>template&lt;&gt; struct compare_3way&lt;void&gt;;</ins>
  <ins>}</ins>  
  [...]
}</code></pre></blockquote>

<p>Add a new clause [cmp.threewaycomparable]. We don't need to add any new semantic constraints. The requirement that the <code class="language-cpp">&lt;=&gt;</code>s used have to be equality-preserving is picked up through [concepts.equality] already.</p>
<blockquote>
<pre class="codehilite"><code class="language-cpp">template &lt;typename T, typename Cat&gt;
  concept compares-as = // exposition only
    Same&lt;common_comparison_category_t&lt;T, Cat&gt;, Cat&gt;;

template &lt;typename T, typename Cat=std::weak_equality&gt;
  concept ThreeWayComparable =
    requires(const remove_reference_t&lt;T&gt;&amp; a,
             const remove_reference_t&lt;T&gt;&amp; b) {
      { a &lt;=&gt; b } -&gt; compares-as&lt;Cat&gt;;
    };

template &lt;typename T, typename U,
          typename Cat=std::weak_equality&gt;
  concept ThreeWayComparableWith = 
    ThreeWayComparable&lt;T, Cat&gt; &amp;&amp;
    ThreeWayComparable&lt;U, Cat&gt; &amp;&amp;
    CommonReference&lt;const remove_reference_t&lt;T&gt;&amp;, const remove_reference_t&lt;U&gt;&amp;&gt; &amp;&amp;
    ThreeWayComparable&lt;
      common_reference_t&lt;const remove_reference_t&lt;T&gt;&amp;, const remove_reference_t&lt;U&gt;&amp;&gt;,
      Cat&gt; &amp;&amp;
    requires(const remove_reference_t&lt;T&gt;&amp; t,
             const remove_reference_t&lt;U&gt;&amp; u) {
      { t &lt;=&gt; u } -&gt; compares-as&lt;Cat&gt;;
      { u &lt;=&gt; t } -&gt; compares-as&lt;Cat&gt;;
    };</code></pre>


</blockquote>
<p>Add a new specification for <code class="language-cpp">compare_3way_type</code> in a new clause after 16.11.3 [cmp.common] named [cmp.3way]:</p>
<blockquote>
<p>The behavior of a program that adds specializations for the <code class="language-cpp">compare_3way_type</code> template defined in this subclause is undefined.</p>
<p>For the <code class="language-cpp">compare_3way_type</code> type trait applied to the types <code class="language-cpp">T</code> and <code class="language-cpp">U</code>, let <code class="language-cpp">t</code> and <code class="language-cpp">u</code> denote lvalues of types <code class="language-cpp">const remove_reference_t&lt;T&gt;</code> and <code class="language-cpp">const remove_reference_t&lt;U&gt;</code>. If the expression <code class="language-cpp">t &lt;=&gt; u</code> is well formed, the member <em>typedef-name</em> <code class="language-cpp">type</code> shall equal <code class="language-cpp">decltype(t &lt;=&gt; u)</code>. Otherwise, there shall be no member <code class="language-cpp">type</code>.</p>
</blockquote>
<p>Add a specification for <code class="language-cpp">compare_3way</code> to a new clause after 16.11.3 [cmp.common] named [cmp.3way]:</p>
<blockquote>
<p>The specializations of <code class="language-cpp">compare_3way</code> for any pointer type yield a strict total order that is consistent among those specializations and is also consistent with the partial order imposed by the built-in operator <code class="language-cpp">&lt;=&gt;</code>. For the template specialization <code class="language-cpp">compare_3way&lt;void&gt;</code>, if the call operator calls a built-in operator comparing pointers, the call operator yields a strict total order that is consistent among those specializations and is also consistent with the partial order imposed by that built-in operator.</p>
<pre class="codehilite"><code class="language-cpp">template&lt;class T = void&gt; struct compare_3way {
  constexpr auto operator()(const T&amp; x, const T&amp; y) const;
};</code></pre>


<p><code class="language-cpp">constexpr auto operator()(const T&amp; x, const T&amp; y) const;</code><br />
<i>Returns</i>: <code class="language-cpp">x &lt;=&gt; y</code>.</p>
<pre class="codehilite"><code class="language-cpp">template&lt;&gt; struct compare_3way&lt;void&gt; {
  template&lt;class T, class U&gt; constexpr auto operator()(T&amp;&amp; t, U&amp;&amp; u) const
    -&gt; decltype(std::forward&lt;T&gt;(t) &lt;=&gt; std::forward&lt;U&gt;(u));

  using is_transparent = unspecified;
};</code></pre>


<p>&nbsp;</p>
<pre class="codehilite"><code class="language-cpp">template&lt;class T, class U&gt; constexpr auto operator()(T&amp;&amp; t, U&amp;&amp; u) const
  -&gt; decltype(std::forward&lt;T&gt;(t) &lt;=&gt; std::forward&lt;U&gt;(u));</code></pre>


<p><i>Returns</i>: <code class="language-cpp">std​::​forward&lt;T&gt;(t) &lt;=&gt; std​::​forward&lt;U&gt;(u)</code>.</p>
</blockquote>
<p>Add the following wording somewhere for <code class="language-cpp">std::ranges::compare_3way</code>, the equivalent of <code class="language-cpp">std::ranges::less</code> for <code class="language-cpp">std::compare_3way</code> and is mostly copied from [range.cmp]. I don't know where it needs to, or how we want to arrange it.</p>
<blockquote>
<p>In this subclause, <code class="language-cpp">BUILTIN_PTR_3WAY(T, U)</code> for types <code class="language-cpp">T</code> and <code class="language-cpp">U</code> is a boolean constant expression. <code class="language-cpp">BUILTIN_PTR_3WAY(T, U)</code> is <code class="language-cpp">true</code> if and only if <code class="language-cpp">&lt;=&gt;</code> in the expression <code class="language-cpp">declval&lt;T&gt;() &lt;=&gt; declval&lt;U&gt;()</code> resolves to a built-in operator comparing pointers.</p>
<p>There is an implementation-defined strict total ordering over all pointer values of a given type. This total ordering is consistent with the partial order imposed by the builtin operator <code class="language-cpp">&lt;=&gt;</code>.</p>
<pre class="codehilite"><code class="language-cpp">template&lt;class T = void&gt;
  requires ThreeWayComparable&lt;T&gt; || Same&lt;T, void&gt; || BUILTIN_PTR_3WAY(const T&amp;, const T&amp;)
struct ranges::compare_3way {
  constexpr auto operator()(const T&amp; x, const T&amp; y) const;
};</code></pre>


<p><code class="language-cpp">operator()</code> has effects equivalent to: <code class="language-cpp">return ranges::compare_3way&lt;&gt;{}(x, y);</code></p>
<pre class="codehilite"><code class="language-cpp">template&lt;&gt; struct ranges::compare_3way&lt;void&gt; {
  template&lt;class T, class U&gt;
    requires ThreeWayComparableWith&lt;T, U&gt; || BUILTIN_PTR_3WAY(T, U)
  constexpr auto operator()(T&amp;&amp; t, U&amp;&amp; u) const;

  using is_transparent = unspecified;
};</code></pre>


<p><em>Expects</em>: If the expression <code class="language-cpp">std::forward&lt;T&gt;(t) &lt;=&gt; std::forward&lt;U&gt;(u)</code> results in a call to a built-in operator <code class="language-cpp">&lt;=&gt;</code> comparing pointers of type <code class="language-cpp">P</code>, the conversion sequences from both <code class="language-cpp">T</code> and <code class="language-cpp">U</code> to <code class="language-cpp">P</code> shall be equality-preserving ([concepts.equality]).</p>
<p><em>Effects</em>: </p>
<ul>
<li>If the expression <code class="language-cpp">std::forward&lt;T&gt;(t) &lt;=&gt; std::forward&lt;U&gt;(u)</code> results in a call to a built-in operator <code class="language-cpp">&lt;=&gt;</code> comparing pointers of type <code class="language-cpp">P</code>: returns <code class="language-cpp">strong_ordering::less</code> if (the converted value of) <code class="language-cpp">t</code> precedes <code class="language-cpp">u</code> in the implementation-defined strict total order over pointers of type <code class="language-cpp">P</code>, <code class="language-cpp">strong_ordering::greater</code> if <code class="language-cpp">u</code> precedes <code class="language-cpp">t</code>, and otherwise <code class="language-cpp">strong_ordering::equal</code>.</li>
<li>Otherwise, equivalent to: <code class="language-cpp">return std::forward&lt;T&gt;(t) &lt;=&gt; std::forward&lt;U&gt;(u);</code></li>
</ul>
</blockquote>
<p>Remove <code class="language-cpp">std::compare_3way()</code> from synopsis in 23.4 [algorithm.syn]:</p>
<blockquote><pre><code>namespace std {
  [...]
  // [alg.3way], three-way comparison algorithms
  <del>template&lt;class T, class U&gt;</del>
  <del>  constexpr auto compare_3way(const T& a, const U& b);</del>
  template&lt;class InputIterator1, class InputIterator2, class Cmp&gt;
    constexpr auto
      lexicographical_compare_3way(InputIterator1 b1, InputIterator1 e1,
                                   InputIterator2 b2, InputIterator2 e2,
                                   Cmp comp)
        -&gt; common_comparison_category_t&lt;decltype(comp(*b1, *b2)), strong_ordering&gt;;
  template&lt;class InputIterator1, class InputIterator2&gt;
    constexpr auto
      lexicographical_compare_3way(InputIterator1 b1, InputIterator1 e1,
                                   InputIterator2 b2, InputIterator2 e2);
  [...]
}</code></pre></blockquote>

<p>Remove the specification of <code class="language-cpp">std::compare_3way()</code> of 23.7.11 [alg.3way]:</p>
<blockquote><del><code>template&lt;class T, class U&gt; constexpr auto compare_3way(const T& a, const U& b);</code>

<p><i>Effects</i>: Compares two values and produces a result of the strongest applicable comparison category type:
<ul>
<li> Returns a <=> b if that expression is well-formed.
<li> Otherwise, if the expressions a == b and a < b are each well-formed and convertible to bool, returns strong_­ordering​::​equal when a == b is true, otherwise returns strong_­ordering​::​less when a < b is true, and otherwise returns strong_­ordering​::​greater.
<li> Otherwise, if the expression a == b is well-formed and convertible to bool, returns strong_­equality​::​equal when a == b is true, and otherwise returns strong_­equality​::​nonequal.
<li>Otherwise, the function is defined as deleted.
</ul></del></blockquote>

<p>Change the specification of <code class="language-cpp">std::lexicographical_compare_3way</code> in 23.7.11 [alg.3way] paragraph 4:</p>
<blockquote><pre><code>template&lt;class InputIterator1, class InputIterator2&gt;
  constexpr auto
    lexicographical_compare_3way(InputIterator1 b1, InputIterator1 e1,
                                 InputIterator2 b2, InputIterator2 e2);</code></pre>

<i>Effects</i>: Equivalent to:
<pre><code>return lexicographical_compare_3way(b1, e1, b2, e2, <ins>compare_3way());</ins>
                                    <del>[](const auto& t, const auto& u) {</del>
                                    <del>  return compare_3way(t, u);</del>
                                    <del>});</del></code></pre>
</blockquote>

<h3 id="alternate-wording-for-stdcompare_3way">3.1. Alternate Wording for <code class="language-cpp">std::compare_3way</code><a class="self-link" href="#alternate-wording-for-stdcompare_3way"></a></h3>
<p>If LEWG decides on just the single, non-template <code class="language-cpp">std::compare_3way</code> function object, the wording can be as follows.</p>
<p>In 16.11.1 [compare.syn]:</p>
<blockquote><pre><code>namespace std {
  [...]

  // [cmp.common], common comparison category type  
  template&lt;class... Ts&gt;
  struct common_comparison_category {
    using type = see below;
  };
  template&lt;class... Ts&gt;
    using common_comparison_category_t = typename common_comparison_category&lt;Ts...&gt;::type;  

  <ins>// [cmp.threewaycomparable], concept ThreeWayComparable
  <ins>template&lt;class T, class Cat = weak_equality&gt;</ins>
    <ins>concept ThreeWayComparable = <i>see below</i>;</ins>
  <ins>template&lt;class T, class U, class Cat = weak_equality&gt;</ins>
    <ins>concept ThreeWayComparableWith = <i>see below</i>;</ins>

  <ins>// [cmp.3way], compare_3way</ins>
  <ins>template&lt;class T, class U = T&gt; struct compare_3way_type;</ins>

  <ins>template&lt;class T, class U = T&gt;</ins>
  <ins>  using compare_3way_type_t = typename compare_3way_type&lt;T, U&gt;::type;</ins>

  <ins>struct compare_3way;</ins>
  [...]
}</code></pre></blockquote>

<p>In the new subclause for <code class="language-cpp">std::compare_3way</code>:</p>
<blockquote>
<p>In this subclause, <code class="language-cpp">BUILTIN_PTR_3WAY(T, U)</code> for types <code class="language-cpp">T</code> and <code class="language-cpp">U</code> is a boolean constant expression. <code class="language-cpp">BUILTIN_PTR_3WAY(T, U)</code> is <code class="language-cpp">true</code> if and only if <code class="language-cpp">&lt;=&gt;</code> in the expression <code class="language-cpp">declval&lt;T&gt;() &lt;=&gt; declval&lt;U&gt;()</code> resolves to a built-in operator comparing pointers.</p>
<p>There is an implementation-defined strict total ordering over all pointer values of a given type. This total ordering is consistent with the partial order imposed by the builtin operator <code class="language-cpp">&lt;=&gt;</code>.</p>
<pre class="codehilite"><code class="language-cpp">struct compare_3way {
  template&lt;class T, class U&gt;
    requires ThreeWayComparableWith&lt;T,U&gt; || BUILTIN_PTR_3WAY(T, U)
  constexpr auto operator()(T&amp;&amp; t, U&amp;&amp;u) const;

  using is_transparent = unspecified;
};</code></pre>


<p><em>Expects</em>: If the expression <code class="language-cpp">std::forward&lt;T&gt;(t) &lt;=&gt; std::forward&lt;U&gt;(u)</code> results in a call to a built-in operator <code class="language-cpp">&lt;=&gt;</code> comparing pointers of type <code class="language-cpp">P</code>, the conversion sequences from both <code class="language-cpp">T</code> and <code class="language-cpp">U</code> to <code class="language-cpp">P</code> shall be equality-preserving ([concepts.equality]).</p>
<p><em>Effects</em>: </p>
<ul>
<li>If the expression <code class="language-cpp">std::forward&lt;T&gt;(t) &lt;=&gt; std::forward&lt;U&gt;(u)</code> results in a call to a built-in operator <code class="language-cpp">&lt;=&gt;</code> comparing pointers of type <code class="language-cpp">P</code>: returns <code class="language-cpp">strong_ordering::less</code> if (the converted value of) <code class="language-cpp">t</code> precedes <code class="language-cpp">u</code> in the implementation-defined strict total order over pointers of type <code class="language-cpp">P</code>, <code class="language-cpp">strong_ordering::greater</code> if <code class="language-cpp">u</code> precedes <code class="language-cpp">t</code>, and otherwise <code class="language-cpp">strong_ordering::equal</code>.</li>
<li>Otherwise, equivalent to: <code class="language-cpp">return std::forward&lt;T&gt;(t) &lt;=&gt; std::forward&lt;U&gt;(u);</code></li>
</ul>
</blockquote>
<h2 id="acknowledgments">4. Acknowledgments<a class="self-link" href="#acknowledgments"></a></h2>
<p>Thank you to Agustín Bergé, Casey Carter and Tim Song for many extensive conversations around these issues. </p>
<h2 id="references">5. References<a class="self-link" href="#references"></a></h2><ul><li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1186r0.html">[P1186R0]</a><span style="margin-left: 5px;">"When do you actually use &lt;=&gt;?" by Barry Revzin, 2018-10-07</span></li><li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1187r0.html">[P1187R0]</a><span style="margin-left: 5px;">"A type trait for std::compare_3way()'s type" by Barry Revzin, 2018-10-07</span></li><li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1252r0.pdf">[P1252R0]</a><span style="margin-left: 5px;">"Ranges Design Cleanup" by Casey Carter, 2018-10-07</span></li><li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1380r0.html">[P1380R0]</a><span style="margin-left: 5px;">"Ambiguity and Insecurities with Three-Way Comparison" by Lawrence Crowl, 2018-11-26</span></li></ul>
</html>