<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>When do you actually use <=>?</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|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|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: P1186R0 <br />
Date: 2018-10-07 <br />
Audience: EWG, LEWG <br />
Reply-To: Barry Revzin, barry dot revzin at gmail dot com <br />
</address>
<hr /><h1 align=center><p>When do you actually use <code class="language-cpp">&lt;=&gt;</code>?</p></h1>
<h2>Contents</h2>
<div class="toc">
<ol>
<li><a href="#motivation">Motivation</a><ol>
<li><a href="#doesnt-quite-work-in-generic-code">Doesn't quite work in generic code</a></li>
<li><a href="#doesnt-quite-work-in-non-generic-code">Doesn't quite work in non-generic code</a></li>
<li><a href="#why-not">... Why not?</a></li>
<li><a href="#when-do-you-actually-write">When do you actually write &lt;=&gt;?</a></li>
<li><a href="#reminiscences-of-invoke">Reminiscences of invoke()</a></li>
</ol>
</li>
<li><a href="#proposal">Proposal</a><ol>
<li><a href="#examples">Examples</a></li>
</ol>
</li>
<li><a href="#counter-arguments">Counter-arguments</a><ol>
<li><a href="#the-initial-premise-is-false-optionalt-shouldnt-always-have">The initial premise is false: optional&lt;T&gt; shouldn't always have &lt;=&gt;</a></li>
<li><a href="#unintentional-comparison-category-strengthening">Unintentional comparison category strengthening</a></li>
</ol>
</li>
<li><a href="#references">References</a></li>
</ol>
</div>

<h2 id="motivation">1. Motivation<a class="self-link" href="#motivation"></a></h2>
<p><a href="https://wg21.link/p0515r3" title="Consistent comparison">P0515</a> introduced <code class="language-cpp">operator&lt;=&gt;</code> as a way of generating all six comparison operators from a single function. But the spaceship operator has some usability issues that still need to be addressed.</p>
<h3 id="doesnt-quite-work-in-generic-code">1.1. Doesn't quite work in generic code<a class="self-link" href="#doesnt-quite-work-in-generic-code"></a></h3>
<p>Following from first principles, it would appear that the way you would implement <code class="language-cpp">&lt;=&gt;</code> for a type like <code class="language-cpp">optional&lt;T&gt;</code> would look like (writing as a non-member function for clarity):</p>
<pre class="codehilite"><code class="language-cpp">template &lt;typename T&gt;
auto operator&lt;=&gt;(optional&lt;T&gt; const&amp; lhs, optional&lt;T&gt; const&amp; rhs)
    -&gt; decltype(*lhs &lt;=&gt; *rhs)
{
    if (lhs.has_value() &amp;&amp; rhs.has_value()) {
        return *lhs &lt;=&gt; *rhs;
    } else {
        return lhs.has_value() &lt;=&gt; rhs.has_value();
    }
}</code></pre>


<p>This is a clean and elegant way of implementing this functionality.</p>
<p>But it is wrong.</p>
<h3 id="doesnt-quite-work-in-non-generic-code">1.2. Doesn't quite work in non-generic code<a class="self-link" href="#doesnt-quite-work-in-non-generic-code"></a></h3>
<p>One of the big selling features of <code class="language-cpp">&lt;=&gt;</code> was the ability to simply default it for types that just want to do normal member-by-member lexicographical comparison. </p>
<p>So it seems like this should work:</p>
<pre class="codehilite"><code class="language-cpp">// some perfectly functional C++17 type that implements a total order
struct Ordered {
    bool operator==(Ordered const&amp;) const { ... }
    bool operator!=(Ordered const&amp;) const { ... }
    bool operator&lt;(Ordered const&amp;) const { ... }
    bool operator&lt;=(Ordered const&amp;) const { ... }
    bool operator&gt;(Ordered const&amp;) const { ... }
    bool operator&gt;=(Ordered const&amp;) const { ... }
};

struct Y {
    int i;
    char c;
    Ordered o;

    auto operator&lt;=&gt;(Y const&amp;) const = default;
};</code></pre>


<p>But this doesn't even compile.</p>
<h3 id="why-not">1.3. ... Why not?<a class="self-link" href="#why-not"></a></h3>
<p>The problem here is, not all types implement <code class="language-cpp">&lt;=&gt;</code>. Indeed, at this moment, only the fundamental types do. So writing any sort of code that relies on the existence of <code class="language-cpp">&lt;=&gt;</code> is highly limited in its functionality. </p>
<p>The provided implementation of <code class="language-cpp">&lt;=&gt;</code> for <code class="language-cpp">optional</code> relies on the existence of <code class="language-cpp">&lt;=&gt;</code> for <code class="language-cpp">T</code>. As a result, while it works great for <code class="language-cpp">optional&lt;int&gt;</code>, it would not be a viable candidate for <code class="language-cpp">optional&lt;Ordered&gt;</code>. Likewise, in order to default the implementation of <code class="language-cpp">&lt;=&gt;</code> for <code class="language-cpp">Y</code>, we need each to perform <code>x<sub>i</sub> <span class="token operator">&lt;=&gt;</span> y<sub>i</sub></code> for each member (see <a href="http://eel.is/c++draft/class.spaceship" title="[class.spaceship]">[class.spaceship]</a>), but as above, <code class="language-cpp">Ordered</code> does not implement <code class="language-cpp">&lt;=&gt;</code>, so this is ill-formed.</p>
<p>What we have to do instead is to use a library function which was also introduced in P0515 but adopted by way of <a href="https://wg21.link/p0768r1" title="Library Support for the Spaceship (Comparison) Operator">P0768R1</a>: <a href="http://eel.is/c++draft/alg.3way" title="[alg.3way]"><code class="language-cpp">std::compare_3way()</code></a>. What this function does is add more fall-back implementations, in a way best illustrated by this skeleton:</p>
<pre class="codehilite"><code class="language-cpp">template&lt;class T, class U&gt;
auto compare_3way(const T&amp; a, const U&amp; b) {
    if constexpr (/* can invoke a &lt;=&gt; b */)
        return a &lt;=&gt; b;
    else if constexpr (/* can invoke a&lt;b and a==b */)
        return a==b ? strong_ordering::equal : a&lt;b ? strong_ordering::less : strong_ordering::greater;
    else if constexpr (/* can invoke a==b */)
        return a == b ? strong_equality::equal : strong_equality::unequal;
    else
        /* ill-formed, defined as deleted */
}</code></pre>


<p>That is, this is an extra library function that lets us use three-way comparisons with types that haven't yet opted into this new language feature. Since the typical case is that types won't have <code class="language-cpp">&lt;=&gt;</code> implemented, we basically want to make sure that we use <code class="language-cpp">compare_3way()</code> for the functionality that we need. In other words, the way we want to implement <code class="language-cpp">&lt;=&gt;</code> for <code class="language-cpp">optional&lt;T&gt;</code> and for <code class="language-cpp">Y</code> is:</p>
<pre class="codehilite" data-line="8,18,19"><code class="language-cpp">template &lt;typename T&gt;
auto operator&lt;=&gt;(optional&lt;T&gt; const&amp; lhs, optional&lt;T&gt; const&amp; rhs)
    -&gt; decltype(compare_3way(*lhs, *rhs))
{
    if (lhs.has_value() &amp;&amp; rhs.has_value()) {
        return compare_3way(*lhs, *rhs);
    } else {
        return compare_3way(lhs.has_value(), rhs.has_value());
    }
}

struct Y {
    int i;
    char c;
    Ordered o;

    strong_ordering operator&lt;=&gt;(Y const&amp; rhs) const {
        if (auto cmp = compare_3way(i, rhs.i); cmp != 0) return cmp;
        if (auto cmp = compare_3way(c, rhs.c); cmp != 0) return cmp;
        return compare_3way(o, rhs.o);
    }
};</code></pre>


<p>Now, there are places in the above code where I <em>could</em> have used <code class="language-cpp">&lt;=&gt;</code>. I know for a fact that it works for <code class="language-cpp">bool</code>, <code class="language-cpp">int</code>, and <code class="language-cpp">char</code> - so the highlighted lines could have used <code class="language-cpp">&lt;=&gt;</code> instead of <code class="language-cpp">compare_3way</code>. But that seems to add so much more cognitive load on the programmer - you have to keep track of what happens to use <code class="language-cpp">&lt;=&gt;</code> and what doesn't. Just writing <code class="language-cpp">compare_3way</code> will call <code class="language-cpp">&lt;=&gt;</code> for you anyway, so why not just always use it?</p>
<p>This is a nuisance for the <code class="language-cpp">optional</code> implementation, but is arguably worse than that for types like <code class="language-cpp">Y</code> above - which merely want to opt-in to default semantics. Not only do I have to manually list the implementation, but note that I also have to manually specify the return type! I cannot rely on <code class="language-cpp">auto</code> to just determine the correct comparison category for me!</p>
<p>Which of course begs the question...</p>
<h3 id="when-do-you-actually-write">1.4. When do you actually write <code class="language-cpp">&lt;=&gt;</code>?<a class="self-link" href="#when-do-you-actually-write"></a></h3>
<p>P0515 states:</p>
<blockquote>
<p><code class="language-cpp">&lt;=&gt;</code> is for type implementers: User code (including generic code) outside the implementation of an <code class="language-cpp">operator&lt;=&gt;</code> should almost never invoke an <code class="language-cpp">&lt;=&gt;</code> directly (as already discovered as a good practice in other languages); for example, code that wants to test <code class="language-cpp">a&lt;b</code> should just write that, not <code class="language-cpp">a&lt;=&gt;b &lt; 0</code>.</p>
</blockquote>
<p>I absolutely agree with the intent of this statement - user code should use binary operators. But as illustrated above, type implementers can't use <code class="language-cpp">&lt;=&gt;</code> either! At least, not until every type the implements an ordering does so by way of <code class="language-cpp">&lt;=&gt;</code>. Which is to say, never.</p>
<p>Effectively, there are exactly two places that can correctly use <code class="language-cpp">&lt;=&gt;</code>:</p>
<ol>
<li>The compiler, which would transform expressions like <code class="language-cpp">a&lt;b</code> into <code class="language-cpp">a&lt;=&gt;b &lt; 0</code>.</li>
<li>The implementation of <code class="language-cpp">std::compare_3way()</code>.</li>
</ol>
<p>That seems a waste of a perfectly good token that we have specially reserved for this occasion. <code class="language-cpp">&lt;=&gt;</code> just seems like a much better spelling than <code class="language-cpp">compare_3way</code>, especially given its ability to be used as an infix operator.</p>
<h3 id="reminiscences-of-invoke">1.5. Reminiscences of <code class="language-cpp">invoke()</code><a class="self-link" href="#reminiscences-of-invoke"></a></h3>
<p>We already have one example where we have a language feature that doesn't quite do everything we need it to do, so we have a library feature that fills in the gaps and thus needs to be used unconditionally: function calls and <code class="language-cpp">std::invoke()</code>. In generic code today, anything constrained with <code class="language-cpp">std::result_of</code> (before C++17), <code class="language-cpp">std::invoke_result</code> or <code class="language-cpp">std::is_invokable</code> (C++17 or later), or <code class="language-cpp">Invocable</code> or <code class="language-cpp">RegularInvocable</code> (C++20 after <a href="https://wg21.link/p0898r3" title="Standard Library Concepts">P0898</a> needs to use <code class="language-cpp">std::invoke()</code> to instead of normal function call syntax. Otherwise, types that meet the constraint but aren't usable with normal function call syntax (i.e. pointers to member functions and pointers to member data) will trigger hard errors. </p>
<p>I continue to view this as an unfortunate split, but we get away with this as a language because pointers to members are fairly rare compared to functions and functions objects, so the usual function call syntax just works the vast majority of the time. Additionally, function call syntax in used in user code all the time. </p>
<p>By contrast with <code class="language-cpp">&lt;=&gt;</code>, the common case is types <em>not</em> supporting <code class="language-cpp">&lt;=&gt;</code> and <code class="language-cpp">&lt;=&gt;</code> won't be commonly used in user code. So it is both true that the gaps that <code class="language-cpp">compare_3way()</code> is filling are much more significant than the equivalent gaps that <code class="language-cpp">invoke()</code> is filling as well as <code class="language-cpp">&lt;=&gt;</code> being less useful in user code than normal function call syntax. </p>
<h2 id="proposal">2. Proposal<a class="self-link" href="#proposal"></a></h2>
<p>This paper proposes to move the entirety of the logic of <code class="language-cpp">compare_3way()</code> into the specification of <code class="language-cpp">operator&lt;=&gt;()</code>. In other words, ignoring parameter inversion, <code class="language-cpp">a &lt;=&gt; b</code> shall mean:</p>
<ol>
<li>Lookup <code class="language-cpp">a.operator&lt;=&gt;(b)</code> and <code class="language-cpp">operator&lt;=&gt;(a, b)</code> as usual for binary operators. If a viable candidate is found, we are done.</li>
<li>Otherwise, if <code class="language-cpp">a == b</code> and <code class="language-cpp">a &lt; b</code> are each well-formed and convertible to <code class="language-cpp">bool</code>, then the expression has type <code class="language-cpp">strong_ordering</code> with value <code class="language-cpp">(a == b) ? strong_ordering::equal : ((a &lt; b) ? strong_ordering::less : strong_ordering::greater)</code></li>
<li>Otherwise, if <code class="language-cpp">a == b</code> is well-formed and convertible to <code class="language-cpp">bool</code>, then the expression has type <code class="language-cpp">strong_equality</code> with value <code class="language-cpp">(a == b) ? strong_equality::equal : strong_equality::nonequal</code>.</li>
<li>Otherwise, the expression is ill-formed</li>
</ol>
<p>This paper additionally proposes to turn <code class="language-cpp">compare_3way</code> into a function object class that simply invokes <code class="language-cpp">&lt;=&gt;</code> on its two arguments, along the lines of <code class="language-cpp">less</code>.</p>
<p>With this change, we would actually be able to use <code class="language-cpp">&lt;=&gt;</code> in all the places where we want to use <code class="language-cpp">&lt;=&gt;</code> today but cannot. We would also be able to default the implementation of <code class="language-cpp">&lt;=&gt;</code> instead of having to manually implement what should be the defaulted implementation.</p>
<h3 id="examples">2.1. Examples<a class="self-link" href="#examples"></a></h3>
<table style="width:100%">
<tr>
<th style="width:50%">
<p>Today (P0515/P0768/C++2a)</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&gt;
auto operator&lt;=&gt;(optional&lt;T&gt; const&amp; lhs,
        optional&lt;T&gt; const&amp; rhs)
    -&gt; decltype(compare_3way(*lhs, *rhs))
{
    if (lhs.has_value() &amp;&amp; rhs.has_value()) {
        return compare_3way(*lhs, *rhs);
    } else {
        return compare_3way(lhs.has_value(),
            rhs.has_value());
    }
}</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">template &lt;typename T&gt;
auto operator&lt;=&gt;(optional&lt;T&gt; const&amp; lhs,
        optional&lt;T&gt; const&amp; rhs)
    -&gt; decltype(*lhs &lt;=&gt; *rhs)
{
    if (lhs.has_value() &amp;&amp; rhs.has_value()) {
        return *lhs &lt;=&gt; *rhs;
    } else {
        return lhs.has_value() &lt;=&gt;
            rhs.has_value();
    }
}</code></pre>
</td>
</tr>
<tr>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">struct Y {
    int i;
    char c;
    Ordered o;

    strong_ordering operator&lt;=&gt;(Y const&amp; rhs) const {
        if (auto cmp = compare_3way(i, rhs.i); cmp != 0) return cmp;
        if (auto cmp = compare_3way(c, rhs.c); cmp != 0) return cmp;
        return compare_3way(o, rhs.o);
    }
};</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">struct Y {
    int i;
    char c;
    Ordered o;

    auto operator&lt;=&gt;(Y const&amp;) const = default;
};</code></pre>
</td>
</tr>    
</table>

<h2 id="counter-arguments">3. Counter-arguments<a class="self-link" href="#counter-arguments"></a></h2>
<p>There are two counter-arguments I'm aware of to this proposal. What follows is an accounting of those arguments and my responses to them. </p>
<h3 id="the-initial-premise-is-false-optionalt-shouldnt-always-have">3.1. The initial premise is false: <code class="language-cpp">optional&lt;T&gt;</code> shouldn't always have <code class="language-cpp">&lt;=&gt;</code><a class="self-link" href="#the-initial-premise-is-false-optionalt-shouldnt-always-have"></a></h3>
<blockquote>
<p><code class="language-cpp">optional&lt;T&gt;</code> only has <code class="language-cpp">&lt;</code> if <code class="language-cpp">T</code> has <code class="language-cpp">&lt;</code>, so <code class="language-cpp">optional&lt;T&gt;</code> should only have <code class="language-cpp">&lt;=&gt;</code> if <code class="language-cpp">T</code> has <code class="language-cpp">&lt;=&gt;</code>. In other words, the initial provided implementation is the correct implementation and the suggested one using <code class="language-cpp">compare_3way()</code> is incorrect.</p>
</blockquote>
<p>There isn't anything particularly specific to <code class="language-cpp">optional</code> in this argument, so we can extend this to: Any compound type should have <code class="language-cpp">&lt;=&gt;</code> only if all of its constituents have <code class="language-cpp">&lt;=&gt;</code>.</p>
<p>This argument sounds seductive, and offers consistency with the way we write other operators. But it has some serious problems. </p>
<p>To start with, the implication here is that <code class="language-cpp">optional&lt;T&gt;</code> (and by extension every compound type) would need now to conditionally implement <em>seven</em> operators (all six pre-existing comparisons, plus <code class="language-cpp">&lt;=&gt;</code>) instead of the advertised <em>one</em> operator. As a result, we lose a big advantage from <code class="language-cpp">&lt;=&gt;</code>: the ability to write less code.</p>
<p>The consequence of writing all seven operators is that it makes <code class="language-cpp">&lt;=&gt;</code> oddly useless. The only code that should invoke <code class="language-cpp">&lt;=&gt;</code> is other types' implementations of <code class="language-cpp">&lt;=&gt;</code>... so in order to invoke <code class="language-cpp">optional&lt;T&gt;</code>'s <code class="language-cpp">&lt;=&gt;</code> for a <code class="language-cpp">T</code> that provides that operator, I would need to have a type that has such a thing as a member. In other words:</p>
<pre class="codehilite"><code class="language-cpp">struct WithSpaceship {
    // ...
    strong_ordering operator&lt;=&gt;(WithSpaceship const&amp; rhs) const {
        // ...
    }
};

struct X {
    optional&lt;WithSpaceship&gt; opt_ws;
    auto operator&lt;=&gt;(X const&amp;) const = default;
};

X x;
x.opt_ws &lt; x.opt_ws; // calls optional's &lt;, not &lt;=&gt;
x &lt; x;               // calls optional's &lt;=&gt; via X's &lt;=&gt;</code></pre>


<p>This is, in of itself, odd. Not only are we not saving code, but the extra operator that we're writing will rarely be used. </p>
<p>But the bigger consequence of this is that we lose the ability to improve performance with three-way comparisons. Typically, <code class="language-cpp">&lt;=&gt;</code> can be better than consecutive calls to <code class="language-cpp">&lt;</code>. Consider <code class="language-cpp">pair&lt;T, T&gt;</code>. If we're only providing <code class="language-cpp">&lt;=&gt;</code> for such a type if <code class="language-cpp">T</code> has <code class="language-cpp">&lt;=&gt;</code>, that means we must still be providing <code class="language-cpp">&lt;</code> if <code class="language-cpp">T</code> has <code class="language-cpp">&lt;</code>. If <code class="language-cpp">T</code> has <code class="language-cpp">&lt;=&gt;</code>, <code class="language-cpp">T</code> has <code class="language-cpp">&lt;</code>, which means that our <code class="language-cpp">pair&lt;T,T&gt;</code> provides everything. </p>
<p>That looks something like this:</p>
<pre class="codehilite"><code class="language-cpp">// &lt; as it exists today, approximately
template &lt;typename T, typename U&gt;
bool operator&lt;(pair&lt;T,U&gt; const&amp; x, pair&lt;T,U&gt; const&amp; y)
{
    return x.first &lt; y.first ||
        (!(y.first &lt; x.first) &amp;&amp; x.second &lt; y.second);
}

// &lt;=&gt; as it would exist based on this reasoning
template &lt;typename T, typename U&gt;
auto operator&lt;=&gt;(pair&lt;T,U&gt; const&amp; x, pair&lt;T,U&gt; const&amp; y)
    -&gt; common_comparison_category_t&lt;...&gt;
{
    if (auto cmp = x.first &lt;=&gt; y.first; cmp != 0) return cmp;
    return x.second &lt;=&gt; y.second;
}

pair&lt;WithSpaceship, WithSpaceship&gt; p = ...;
p &lt; p;    // calls pair::operator&lt;
p &lt;=&gt; p;  // calls pair::operator&lt;=&gt;</code></pre>


<p><code class="language-cpp">WithSpaceship</code> has an efficient <code class="language-cpp">&lt;=&gt;</code>, but <code class="language-cpp">pair</code>'s <code class="language-cpp">operator&lt;</code> can't use it! Which means that <code class="language-cpp">p &lt; p</code> has to call <code class="language-cpp">&lt;</code> three times, which in turn calls <code class="language-cpp">WithSpaceship::operator&lt;=&gt;</code> three times. But <code class="language-cpp">p &lt;=&gt; p</code> only has to call <code class="language-cpp">&lt;=&gt;</code> twice.</p>
<p>This argument could be extended out to <code class="language-cpp">vector&lt;T&gt;</code>'s <code class="language-cpp">operator&lt;</code> which now potentially does up to <code class="language-cpp">2N-1</code> calls to <code class="language-cpp">T</code>'s <code class="language-cpp">&lt;</code> instead of up to <code class="language-cpp">N</code> calls to <code class="language-cpp">T</code>'s <code class="language-cpp">&lt;=&gt;</code>. That could be a serious pessimization - one that would encourage people to actually write <code class="language-cpp">&lt;=&gt;</code> in code! After all, if <code class="language-cpp">(a &lt;=&gt; b) &lt; 0</code> could potentially be faster than <code class="language-cpp">a &lt; b</code>, why would I write the latter? That would be a serious design error. </p>
<p>In short, only conditionally providing <code class="language-cpp">&lt;=&gt;</code> for compound types not only defeats the goal of writing less code, but also defeats the goal of writing more performant code. I think the only conclusion is that compound types need to provide <code class="language-cpp">&lt;=&gt;</code> whenever their underlying types are comparable at all and be an unconditional substitute for the other relational operators.</p>
<h3 id="unintentional-comparison-category-strengthening">3.2. Unintentional comparison category strengthening<a class="self-link" href="#unintentional-comparison-category-strengthening"></a></h3>
<blockquote>
<p>When a class author implements <code class="language-cpp">&lt;=&gt;</code> for their type, they have to decide what comparison category to use as the return type. Other code could use that choice to make important decisions. But if we had <code class="language-cpp">&lt;=&gt;</code> fall-back to <code class="language-cpp">compare_3way()</code>, we effectively are guessing what the intended comparison category was. <code class="language-cpp">decltype(x &lt;=&gt; y)</code> might be a deliberate choice of the class author - or it might be compiler inference, and we can't tell. </p>
<p>Moreover, we might get this wrong in a very confusing way that inadvertently strengthens the comparison category. For example, the following code is well-formed:</p>
<pre class="codehilite"><code class="language-cpp">bool foo(error_code const&amp; a, error_condition const&amp; b) {
    return a == b;
}</code></pre>


<p>because there exists an equality comparison between these <a href="http://eel.is/c++draft/syserr.compare" title="[syserr.compare]">two types</a>. These two types mean rather different things and obviously are not substitutable, but the proposed changed would nevertheless give <code class="language-cpp">decltype(a &lt;=&gt; b)</code> the type <code class="language-cpp">strong_equality</code> and that is a very misleading and strongly undesired result. </p>
</blockquote>
<p>I have two responses to this argument. </p>
<p>First, practically speaking there is no difference between using <code class="language-cpp">a &lt;=&gt; b</code> in this context and getting <code class="language-cpp">strong_equality</code> and using <code class="language-cpp">compare_3way(a, b)</code> in this context and getting <code class="language-cpp">strong_equality</code>. The end result is the same - equally misleading, and the added weight on the meaning of <code class="language-cpp">&lt;=&gt;</code> here is a distinction without a difference. In today's world, people will write <code class="language-cpp">&lt;=&gt;</code> when they need a three-way comparison, find that it doesn't work, and then switch to <code class="language-cpp">compare_3way()</code> - because <code class="language-cpp">compare_3way()</code> solves a problem. </p>
<p>Moreover, even if we maintained that <code class="language-cpp">a &lt;=&gt; b</code> is ill-formed (as it is today), that wouldn't change the fact that the provided valid implementation of <code class="language-cpp">optional</code> using <code class="language-cpp">compare_3way()</code> would give already us <code class="language-cpp">optional(a) &lt;=&gt; optional(b)</code> as a valid operation whose type is <code class="language-cpp">strong_equality</code>. As argued in the previous section, <code class="language-cpp">optional</code> needs to implement <code class="language-cpp">&lt;=&gt;</code>, and it needs to implement it in this way, so this comparison category strengthening already exists as an issue. I'm unconvinced that <em>specifically</em> <code class="language-cpp">&lt;=&gt;</code> yielding a misleading response <em>specifically</em> on the underlying types is a problem.</p>
<p>Second, the fact that <code class="language-cpp">compare_3way(a, b)</code> today and <code class="language-cpp">a &lt;=&gt; b</code> with this proposal yields <code class="language-cpp">strong_equality</code> is a problem - but it's not <code class="language-cpp">&lt;=&gt;</code>'s problem, it's <code class="language-cpp">error_code</code>'s problem. The decision to use <code class="language-cpp">==</code> in this context is arguably a bad design decision. With the direction the standard library is going and the new concepts in Ranges in <a href="https://wg21.link/p0898r3" title="Standard Library Concepts">P0898</a> (both terminology concepts and language <code class="language-cpp">concept</code>s), we are adding semantic requirements on top of syntactic ones - and the lack of substitutability between <code class="language-cpp">error_code</code> and <code class="language-cpp">error_condition</code> means that we're meeting the syntactic but not the semantic requirements of <code class="language-cpp">EqualityComparableWith</code>. This is bad.</p>
<p>Jonathan Müller recently wrote an in-depth series of blog posts on the <a href="https://foonathan.net/blog/2018/09/07/three-way-comparison.html" title="foonathan::blog() - Mathematics behind Mathematics behind Comparison #4: Three-Way Comparison">mathematics behind comparisons</a> which makes the compelling argument that the existence of <code class="language-cpp">==</code> should be <code class="language-cpp">strong_equality</code> only, and the existence of <code class="language-cpp">==</code> and <code class="language-cpp">&lt;</code> should be <code class="language-cpp">strong_ordering</code> only. Any other required comparison category should be a named function instead. This argument is very much in line with the principles behind Ranges. </p>
<p>The conclusion of this is that yes, there is a problem. But the problem lies with <code class="language-cpp">error_code</code> and <code class="language-cpp">error_condition</code> for violating expectations with the decision to improperly use <code class="language-cpp">==</code> when it doesn't mean equality and substitutability. Having <code class="language-cpp">&lt;=&gt;</code> fall back to guessing at the comparison category and yielding <code class="language-cpp">strong_ordering</code> or <code class="language-cpp">strong_equality</code> works for types that follow good design guidance in their choice of using operators. </p>
<p>Note that this problem could easily be fixed by replacing the currently existing <code class="language-cpp">==</code> and <code class="language-cpp">!=</code> for these types with a <code class="language-cpp">&lt;=&gt;</code> returning <code class="language-cpp">weak_equality</code>. This is still a questionable choice, but at least you would observe the correct comparison category without otherwise breaking user code. Ideally, <code class="language-cpp">==</code> and <code class="language-cpp">!=</code> get replaced with a named function that itself returns <code class="language-cpp">weak_equality</code>.</p>
<p>This leaves the question of whether or not <code class="language-cpp">&lt;=&gt;</code> was explicitly provided or inferred. In practice, I think this is about as relevant as whether or not the copy constructor was explicit or compiler generated (or more relevantly, as whether or not <code class="language-cpp">&lt;</code> was generated from <code class="language-cpp">&lt;=&gt;</code> or from <code class="language-cpp">&lt;</code>). As long as it has sane semantics. However, if people feel strongly about wanting this particular piece of information, if we're already adding language magic to have <code class="language-cpp">&lt;=&gt;</code> perform multiple possible operations, it is surely possible to add language magic to directly retrieve the type of the actual binary <code class="language-cpp">&lt;=&gt;</code> operation if and only if it exists. </p>
<h2 id="references">4. References<a class="self-link" href="#references"></a></h2><ul><li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0515r3.pdf">[P0515R3]</a><span style="margin-left: 5px;">"Consistent comparison" by Herb Sutter, Jens Maurer, Walter E. Brown, 2017-11-10</span></li><li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0768r1.pdf">[P0768R1]</a><span style="margin-left: 5px;">"Library Support for the Spaceship (Comparison) Operator" by Walter E. Brown, 2017-11-10</span></li><li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0898r3.pdf">[P0898R3]</a><span style="margin-left: 5px;">"Standard Library Concepts" by Casey Carter, Eric Niebler, 2018-06-08</span></li><li><a href="http://eel.is/c++draft/alg.3way">[alg.3way]</a><span style="margin-left: 5px;">Current Working Draft</span></li><li><a href="http://eel.is/c++draft/class.spaceship">[class.spaceship]</a><span style="margin-left: 5px;">Current Working Draft</span></li><li><a href="https://foonathan.net/blog/2018/09/07/three-way-comparison.html">[muller.compare]</a><span style="margin-left: 5px;">"foonathan::blog() - Mathematics behind Mathematics behind Comparison #4: Three-Way Comparison" by Jonathan Müller, 2018-09-07</span></li><li><a href="http://eel.is/c++draft/syserr.compare">[syserr.compare]</a><span style="margin-left: 5px;">Current Working Draft</span></li></ul>
</html>