<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><=> != ==</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: P1185R0 <br />
Date: 2018-10-07 <br />
Audience: EWG <br />
Reply-To: Barry Revzin, barry dot revzin at gmail dot com <br />
</address>
<hr /><h1 align=center><p><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="#why-this-is-really-bad">Why this is really bad</a></li>
<li><a href="#other-languages">Other Languages</a><ol>
<li><a href="#rust">Rust</a></li>
<li><a href="#other-languages_1">Other Languages</a></li>
</ol>
</li>
</ol>
</li>
<li><a href="#proposal">Proposal</a><ol>
<li><a href="#change-the-candidate-set-for-operator-lookup">Change the candidate set for operator lookup</a></li>
<li><a href="#change-the-meaning-of-defaulted-equality-operators">Change the meaning of defaulted equality operators</a></li>
<li><a href="#change-how-we-define-strong-structural-equality">Change how we define strong structural equality</a></li>
<li><a href="#change-defaulted-to-also-generate-a-defaulted">Change defaulted &lt;=&gt; to also generate a defaulted ==</a></li>
</ol>
</li>
<li><a href="#important-implications">Important implications</a><ol>
<li><a href="#implications-for-types-that-have-special-but-not-different-comparisons">Implications for types that have special, but not different, comparisons</a></li>
<li><a href="#implications-for-comparison-categories">Implications for comparison categories</a></li>
</ol>
</li>
<li><a href="#wording">Wording</a><ol>
<li><a href="#wording-for-redefining-strong-structural-equality">Wording for redefining strong structural equality</a></li>
<li><a href="#wording-for-defaulted-generating-a-defaulted">Wording for defaulted &lt;=&gt; generating a defaulted ==</a></li>
</ol>
</li>
<li><a href="#acknowledgements">Acknowledgements</a></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, as well as the ability to default this so as to avoid writing any code at all. See David Stone's <a href="https://github.com/davidstone/isocpp/blob/b2db8e00dfec04a7742c67a5ea6e9575c9aba03d/operator-spaceship/I-did-not-order-this.md" title="I did not order this! Why is it on my bill?">I did not order this!</a> for a very clear, very thorough description of the problem: it does not seem to be possible to implement <code class="language-cpp">&lt;=&gt;</code> optimally for "wrapper" types. What follows is a super brief run-down.</p>
<p>Consider a type like:</p>
<pre class="codehilite"><code class="language-cpp">struct S {
    vector&lt;string&gt; names;
    auto operator&lt;=&gt;(S const&amp;) const = default;
};</code></pre>


<p>Today, this is ill-formed, because <code class="language-cpp">vector</code> does not implement <code class="language-cpp">&lt;=&gt;</code>. In order to make this work, we need to add that implementation. It is <em>not</em> recommended that <code class="language-cpp">vector</code> only provide <code class="language-cpp">&lt;=&gt;</code>, but we will start there and it will become clear why that is the recommendation.</p>
<p>The most straightforward implementation of <code class="language-cpp">&lt;=&gt;</code> for <code class="language-cpp">vector</code> is (let's just assume <code class="language-cpp">strong_ordering</code> and note that I'm deliberately not using <code class="language-cpp">std::lexicographical_compare_3way()</code> for clarity):</p>
<pre class="codehilite"><code class="language-cpp">template&lt;typename T&gt;
strong_ordering operator&lt;=&gt;(vector&lt;T&gt; const&amp; lhs, vector&lt;T&gt; const&amp; rhs) {
    size_t min_size = min(lhs.size(), rhs.size());
    for (size_t i = 0; i != min_size; ++i) {
        if (auto const cmp = compare_3way(lhs[i], rhs[i]); cmp != 0) {
            return cmp;
        }
    }
    return lhs.size() &lt;=&gt; rhs.size();
}</code></pre>


<p>On the one hand, this is great. We wrote one function instead of six, and this function is really easy to understand too. On top of that, this is a really good implementation for <code class="language-cpp">&lt;</code>!  As good as you can get. And our code for <code class="language-cpp">S</code> works (assuming we do something similar for <code class="language-cpp">string</code>).</p>
<p>On the other hand, as David goes through in a lot of detail (seriously, read it) this is quite bad for <code class="language-cpp">==</code>. We're failing to short-circuit early on size differences! If two containers have a large common prefix, despite being different sizes, that's an enormous amount of extra work!</p>
<p>In order to do <code class="language-cpp">==</code> efficiently, we have to short-circuit and do <code class="language-cpp">==</code> all the way down. That is:</p>
<pre class="codehilite"><code class="language-cpp">template&lt;typename T&gt;
bool operator==(vector&lt;T&gt; const&amp; lhs, vector&lt;T&gt; const&amp; rhs)
{
    // short-circuit on size early
    const size_t size = lhs.size();
    if (size != rhs.size()) {
        return false;
    }

    for (size_t i = 0; i != size; ++i) {
        // use ==, not &lt;=&gt;, in all nested comparisons
        if (lhs[i] != rhs[i]) {
            return false;
        }
    }

    return true;
}</code></pre>


<h3 id="why-this-is-really-bad">1.1. Why this is really bad<a class="self-link" href="#why-this-is-really-bad"></a></h3>
<p>This is really bad on several levels, significant levels.</p>
<p>First, since <code class="language-cpp">==</code> falls back on <code class="language-cpp">&lt;=&gt;</code>, it's easy to fall into the trap that once <code class="language-cpp">v1 == v2</code> compiles and gives the correct answer, we're done. If we didn't implement the efficient <code class="language-cpp">==</code>, outside of very studious code review, we'd have no way of finding out. The problem is that <code class="language-cpp">v1 &lt;=&gt; v2 == 0</code> would always give the <em>correct</em> answer (assuming we correctly implemented <code class="language-cpp">&lt;=&gt;</code>). How do you write a test to ensure that we did the short circuiting? The only way you could do it is to time some pathological case - comparing a vector containing a million entries against a vector containing those same million entries plus <code class="language-cpp">1</code> - and checking if it was fast?</p>
<p>Second, the above isn't even complete yet. Because even if we were careful enough to write <code class="language-cpp">==</code>, we'd get an efficient <code class="language-cpp">v1 == v2</code>... but still an inefficient <code class="language-cpp">v1 != v2</code>, because that one would call <code class="language-cpp">&lt;=&gt;</code>. We would have to also write this manually:</p>
<pre class="codehilite"><code class="language-cpp">template&lt;typename T&gt;
bool operator!=(vector&lt;T&gt; const&amp; lhs, vector&lt;T&gt; const&amp; rhs)
{
    return !(lhs == rhs);
}</code></pre>


<p>Third, this compounds <em>further</em> for any types that have something like this as a member. Getting back to our <code class="language-cpp">S</code> above:</p>
<pre class="codehilite"><code class="language-cpp">struct S {
    vector&lt;string&gt; names;
    auto operator&lt;=&gt;(S const&amp;) const = default;
};</code></pre>


<p>Even if we correctly implemented <code class="language-cpp">==</code>, <code class="language-cpp">!=</code>, and <code class="language-cpp">&lt;=&gt;</code> for <code class="language-cpp">vector</code> and <code class="language-cpp">string</code>, comparing two <code class="language-cpp">S</code>s for equality <em>still</em> calls <code class="language-cpp">&lt;=&gt;</code> and is <em>still</em> a completely silent pessimization. Which <em>again</em> we cannot test functionally, only with a timer.</p>
<p>And then, it somehow gets even worse, because it's be easy to fall into yet another trap: you somehow have the diligence to remember that you need to explicitly define <code class="language-cpp">==</code> for this type and you do it this way:</p>
<pre class="codehilite"><code class="language-cpp">struct S {
    vector&lt;string&gt; names;
    auto operator&lt;=&gt;(S const&amp;) const = default;
    bool operator==(S const&amp;) const = default; // problem solved, right?
};</code></pre>


<p>But what does defaulting <code class="language-cpp">operator==</code> actually do? It <a href="http://eel.is/c++draft/class.rel.eq" title="[class.rel.eq]">invokes <code class="language-cpp">&lt;=&gt;</code></a>. So here's explicit code that seems sensible to add to attempt to address this problem, that does absolutely nothing to address this problem. </p>
<p>The only way to get efficiency is to have every type, even <code class="language-cpp">S</code> above, implement both not just <code class="language-cpp">&lt;=&gt;</code> but also <code class="language-cpp">==</code> and <code class="language-cpp">!=</code>. By hand. </p>
<pre class="codehilite"><code class="language-cpp">struct S {
    vector&lt;string&gt; names;
    auto operator&lt;=&gt;(S const&amp;) const = default;
    bool operator==(S const&amp; rhs) const { return names == rhs.names; }
    bool operator!=(S const&amp; rhs) const { return names != rhs.names; }
};</code></pre>


<p>That is the status quo today and the problem that needs to be solved.</p>
<h3 id="other-languages">1.2. Other Languages<a class="self-link" href="#other-languages"></a></h3>
<p>In order how to best figure out how to solve this problem for C++, it is helpful to look at how other languages have already addressed this issue. While P0515 listed many languages which have a three-way comparison returning a signed integer, there is another set of otherwise mostly-unrelated languages that take a different approach. </p>
<h4 id="rust">1.2.1. Rust<a class="self-link" href="#rust"></a></h4>
<p>Rust, Kotlin, Swift, Haskell, and Scala are rather different languages in many respects. But they all solve this particular problem in basically the same way: they treat <em>equality</em> and <em>comparison</em> as separate operations. I want to focus specifically on Rust here as it's arguably the closest language to C++ of the group, but the other three are largely equivalent for the purposes of this specific discussion.</p>
<p>Rust deals in Traits (which are roughly analogous to C++0x concepts and Swift protocols) and it has four relevant Traits that have to do with comparisons:</p>
<ul>
<li><code class="language-cpp">PartialEq</code> (which is a partial equivalence relation spelled which only requires symmetry and transitivity)</li>
<li><code class="language-cpp">Eq</code> (which extends <code class="language-cpp">PartialEq</code>, adding reflexivity)</li>
<li><code class="language-cpp">PartialOrd</code> (which allows for incomparability by returning <code class="language-cpp">Option&lt;Ordering&gt;</code>, where <code class="language-cpp">Ordering</code> is an enum)</li>
<li><code class="language-cpp">Ord</code> (a total order, which extends <code class="language-cpp">Eq</code> and <code class="language-cpp">PartialOrd</code>)</li>
</ul>
<p>The actual operators are <a href="https://doc.rust-lang.org/reference/expressions/operator-expr.html#comparison-operators" title="Comparison Operators - The Rust Reference">implicitly generated</a> from these traits, but not all from the same one. Importantly, <code class="language-cpp">x == y</code> is translated as <code class="language-cpp">PartialEq::eq(x, y)</code> whereas <code class="language-cpp">x &lt; y</code> is translated as <code class="language-cpp">PartialOrd::lt(x, y)</code> (which is effectively checking that <code class="language-cpp">PartialOrd::partial_cmp(x, y)</code> is <code class="language-cpp">Less</code>).</p>
<p>That is, you don't get <em>six</em> functions for the price of one. You need to write <em>two functions</em>. </p>
<p>Even if you don't know Rust (and I really don't know Rust), I think it would be instructive here would be to look at how the equivalent comparisons are implemented for Rust's <code class="language-cpp">vector</code> type. The important parts look like this:</p>
<table style="width:100%">
<tr>
<th style="width:50%">
<p><a href="https://doc.rust-lang.org/src/core/slice/mod.rs.html?search=#4037-4053" title="Implementation of Eq for Slice"><code class="language-cpp">Eq</code></a></p>
</th>
<th>
<p><a href="https://doc.rust-lang.org/src/core/slice/mod.rs.html#4116-4136" title="Implementation of Ord for Slice"><code class="language-cpp">Ord</code></a></p>
</th>
</tr>
<tr>
<td>
<pre style="background:transparent;border:0px" data-line="5,6,7,10"><code class="language-rust">impl&lt;A, B&gt; SlicePartialEq&lt;B&gt; for [A]
    where A: PartialEq&lt;B&gt;
{
    default fn eq(&amp;self, other: &amp;[B]) -&gt; bool {
        if self.len() != other.len() {
            return false;
        }

        for i in 0..self.len() {
            if !self[i].eq(&amp;other[i]) {
                return false;
            }
        }

        true
    }
}</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px" data-line="11"><code class="language-rust">impl&lt;A&gt; SliceOrd&lt;A&gt; for [A]
    where A: Ord
{
    default fn cmp(&amp;self, other: &amp;[A]) -&gt; Ordering {
        let l = cmp::min(self.len(), other.len());

        let lhs = &amp;self[..l];
        let rhs = &amp;other[..l];

        for i in 0..l {
            match lhs[i].cmp(&amp;rhs[i]) {
                Ordering::Equal =&gt; (),
                non_eq =&gt; return non_eq,
            }
        }

        self.len().cmp(&amp;other.len())
    }
}</code></pre>
</td>
</tr>
</table>

<p>In other words, <code class="language-cpp">eq</code> calls <code class="language-cpp">eq</code> all the way down while doing short-circuiting whereas <code class="language-cpp">cmp</code> calls <code class="language-cpp">cmp</code> all the way down, and these are two separate functions. Both algorithms exactly match our implementation of <code class="language-cpp">==</code> and <code class="language-cpp">&lt;=&gt;</code> for <code class="language-cpp">vector</code> above. Even though <code class="language-cpp">cmp</code> performs a 3-way ordering, and you can use the result of <code class="language-cpp">a.cmp(b)</code> to determine that <code class="language-cpp">a == b</code>, it is <em>not</em> the way that Rust (or other languages in this realm like Swift and Kotlin and Haskell) determine equality. </p>
<h4 id="other-languages_1">1.2.2. Other Languages<a class="self-link" href="#other-languages_1"></a></h4>
<p>Swift has <a href="https://developer.apple.com/documentation/swift/equatable" title="Equatable - Swift Standard Library"><code class="language-cpp">Equatable</code></a> and <a href="https://developer.apple.com/documentation/swift/comparable" title="Comparable - Swift Standard Library"><code class="language-cpp">Comparable</code></a> protocols. For types that conform to <code class="language-cpp">Equatable</code>, <code class="language-cpp">!=</code> is implicitly generated from <code class="language-cpp">==</code>. For types that conform to <code class="language-cpp">Comparable</code>, <code class="language-cpp">&gt;</code>, <code class="language-cpp">&gt;=</code>, and <code class="language-cpp">&lt;=</code> are implicitly generated from <code class="language-cpp">&lt;</code>. Swift does not have a 3-way comparison function.</p>
<p>There are other languages that make roughly the same decision in this regard that Rust does: <code class="language-cpp">==</code> and <code class="language-cpp">!=</code> are generated from a function that does equality whereas the four relational operators are generated from a three-way comparison. Even though the three-way comparison <em>could</em> be used to determine equality, it is not:</p>
<ul>
<li>Kotlin, like Java, has a <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-comparable/index.html" title="Comparable - Kotlin Programming Language"><code class="language-cpp">Comparable</code></a> interface and a separate <code class="language-cpp">equals</code> method inherited from <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html" title="Any - Kotlin Programming Language"><code class="language-cpp">Any</code></a>. Unlike Java, it has <a href="https://kotlinlang.org/docs/reference/operator-overloading.html#equals" title="Operator overloading - Kotlin Programming Language">operator overloading</a>: <code class="language-cpp">a == b</code> means <code class="language-cpp">a?.equals(b) ?: (b === null)</code> and <code class="language-cpp">a &lt; b</code> means <code class="language-cpp">a.compareTo(b) &lt; 0</code>.</li>
<li>Haskell has the <a href="http://hackage.haskell.org/package/base-4.11.1.0/docs/Data-Eq.html" title="Data.Eq - Haskell documentation"><code class="language-cpp">Data.Eq</code></a> and <a href="http://hackage.haskell.org/package/base-4.11.1.0/docs/Data-Ord.html" title="Data.Ord - Haskell documentation"><code class="language-cpp">Data.Ord</code></a> type classes. <code class="language-cpp">!=</code> is generated from <code class="language-cpp">==</code> (or vice versa, depending on which definition is provided for <code class="language-cpp">Eq</code>). If a <code class="language-cpp">compare</code> method is provided to conform to <code class="language-cpp">Ord</code>, <code class="language-cpp">a &lt; b</code> means <code class="language-cpp">(compare a b) &lt; 0</code>.</li>
<li>Scala's equality operators come from the root <a href="https://www.scala-lang.org/api/current/scala/Any.html#==(x$1:Any):Boolean" title="Scala Standard Library - Any"><code class="language-cpp">Any</code></a> interface, <code class="language-cpp">a == b</code> means <code class="language-cpp">if (a eq null) b eq null else a.equals(b)</code>. Its relational operators come from the <a href="https://www.scala-lang.org/api/current/scala/math/Ordered.html" title="Scala Standard Library - Ordered"><code class="language-cpp">Ordered</code></a> trait, where <code class="language-cpp">a &lt; b</code> means <code class="language-cpp">(a compare b) &lt; 0</code>.</li>
</ul>
<h2 id="proposal">2. Proposal<a class="self-link" href="#proposal"></a></h2>
<p>Fundamentally, we have two sets of operations: equality and comparison. In order to be efficient and not throw away performance, we need to implement them separately. <code class="language-cpp">operator&lt;=&gt;()</code> as specified in the working draft today generating all six functions just doesn't seem to be a good solution.</p>
<p>This paper proposes to do something similar to the Rust model above and first described in <a href="https://github.com/davidstone/isocpp/blob/master/operator-spaceship/I-did-not-order-this.md#make-operator-create-only-operator-operator-operator-and-operator">this section</a> of the previously linked paper: require two separate functions to implement all the functionality. </p>
<p>The proposal has two core components:</p>
<ul>
<li>change the candidate set for operator lookup</li>
<li>change the meaning of defaulted equality operators</li>
</ul>
<p>And two optional components:</p>
<ul>
<li>change how we define <a href="http://eel.is/c++draft/class.compare#def:equality,strong_structural" title="[class.compare]"><em>strong structural equality</em></a>, which is important for <a href="https://wg21.link/p0732r2" title="Class Types in Non-Type Template Parameters">P0732R2</a></li>
<li>change defaulted <code class="language-cpp">&lt;=&gt;</code> to also generate a defaulted <code class="language-cpp">==</code></li>
</ul>
<h3 id="change-the-candidate-set-for-operator-lookup">2.1. Change the candidate set for operator lookup<a class="self-link" href="#change-the-candidate-set-for-operator-lookup"></a></h3>
<p>Today, lookup for any of the relational and equality operators will also consider <a href="http://eel.is/c++draft/over.match.oper#3.4"><code class="language-cpp">operator&lt;=&gt;</code></a>, but preferring <a href="http://eel.is/c++draft/over.match.best#1.10">the actual used operator</a>. </p>
<p>The proposed change is for the equality operators to <em>not</em> consider <code class="language-cpp">&lt;=&gt;</code> candidates. Instead, inequality will consider equality as a candidate. In other words, here is the proposed set of candidates. There are no changes proposed for the relational operators, only for the equality ones:</p>
<table>
<th>
<p>Source <br />
<code class="language-cpp">a @ b</code></p>
</th>
<th>
<p>Today (P0515/C++2a)</p>
</th>
<th>
<p>Proposed</p>
</th>
</tr>
<tr>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">a == b</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">a == b
(a &lt;=&gt; b) == 0
0 == (b &lt;=&gt; a)</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">a == b
b == a</code></pre>
</td>
</tr>
<tr>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">a != b</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">a != b
(a &lt;=&gt; b) != 0
0 != (a &lt;=&gt; b)</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">a != b
!(a == b)
!(b == a)</code></pre>
</td>
</tr>
<tr>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">a &lt; b</code></pre>
</td>
<td colspan="2">
<pre style="background:transparent;border:0px"><code class="language-cpp">a &lt; b
(a &lt;=&gt; b) &lt; 0
0 &lt; (b &lt;=&gt; a)</code></pre>
</td>
</tr>
<tr>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">a &lt;= b</code></pre>
</td>
<td colspan="2">
<pre style="background:transparent;border:0px"><code class="language-cpp">a &lt;= b
(a &lt;=&gt; b) &lt;= 0
0 &lt;= (b &lt;=&gt; a)</code></pre>
</td>
</tr>
<tr>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">a &gt; b</code></pre>
</td>
<td colspan="2">
<pre style="background:transparent;border:0px"><code class="language-cpp">a &gt; b
(a &lt;=&gt; b) &gt; 0
0 &gt; (b &lt;=&gt; a)</code></pre>
</td>
</tr>
<tr>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">a &gt;= b</code></pre>
</td>
<td colspan="2">
<pre style="background:transparent;border:0px"><code class="language-cpp">a &gt;= b
(a &lt;=&gt; b) &gt;= 0
0 &gt;= (b &lt;=&gt; a)</code></pre>
</td>
</tr>
</table>

<p>In short, <code class="language-cpp">==</code> and <code class="language-cpp">!=</code> never invoke <code class="language-cpp">&lt;=&gt;</code> implicitly. </p>
<h3 id="change-the-meaning-of-defaulted-equality-operators">2.2. Change the meaning of defaulted equality operators<a class="self-link" href="#change-the-meaning-of-defaulted-equality-operators"></a></h3>
<p>As mentioned earlier, in the current working draft, defaulting <code class="language-cpp">==</code> or <code class="language-cpp">!=</code> generates a function that invokes <code class="language-cpp">&lt;=&gt;</code>. This paper proposes that defaulting <code class="language-cpp">==</code> generates a member-wise equality comparison and that defaulting <code class="language-cpp">!=</code> generate a call to negated <code class="language-cpp">==</code>.</p>
<p>That is:</p>
<table style="width:100%">
<tr>
<th style="width:33%">
<p>Sample Code</p>
</th>
<th style="width:33%">
<p>Meaning Today (P0515/C++2a)</p>
</th>
<th>
<p>Proposed Meaning</p>
</th>
</tr>
<tr>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">struct X {
  A a;
  B b;
  C c;

  auto operator&lt;=&gt;(X const&amp;) const = default;
  bool operator==(X const&amp;) const = default;
  bool operator!=(X const&amp;) const = default;
};</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">struct X {
  A a;
  B b;
  C c;

  ??? operator&lt;=&gt;(X const&amp; rhs) const {
    if (auto cmp = a &lt;=&gt; rhs.a; cmp != 0)
      return cmp;
    if (auto cmp = b &lt;=&gt; rhs.b; cmp != 0)
      return cmp;
    return c &lt;=&gt; rhs.c;
  }

  bool operator==(X const&amp; rhs) const {
    return (*this &lt;=&gt; rhs) == 0;
  }

  bool operator!=(X const&amp; rhs) const {
    return (*this &lt;=&gt; rhs) != 0;
  }
};</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">struct X {
  A a;
  B b;
  C c;

  ??? operator&lt;=&gt;(X const&amp; rhs) const {
    if (auto cmp = a &lt;=&gt; rhs.a; cmp != 0)
      return cmp;
    if (auto cmp = b &lt;=&gt; rhs.b; cmp != 0)
      return cmp;
    return c &lt;=&gt; rhs.c;
  }

  bool operator==(X const&amp; rhs) const {
    return a == rhs.a &amp;&amp;
      b == rhs.b &amp;&amp;
      c == rhs.c;
  }

  bool operator!=(X const&amp; rhs) const {
    return !(*this == rhs);
  }
};</code></pre>
</td>
</tr>
</table>

<p>These two changes ensure that the equality operators and the relational operators remain segregated. </p>
<h3 id="change-how-we-define-strong-structural-equality">2.3. Change how we define strong structural equality<a class="self-link" href="#change-how-we-define-strong-structural-equality"></a></h3>
<p><a href="https://wg21.link/p0732r2" title="Class Types in Non-Type Template Parameters">P0732R2</a> relies on <em>strong structural equality</em> as the criteria to allow a class to be used as a non-type template parameter - which is based on having a defaulted <code class="language-cpp">&lt;=&gt;</code> that itself only calls defaulted <code class="language-cpp">&lt;=&gt;</code> recursively all the way down and has type either <code class="language-cpp">strong_ordering</code> or <code class="language-cpp">strong_equality</code>.</p>
<p>This criteria clashes somewhat with this proposal, which is fundamentally about not making <code class="language-cpp">&lt;=&gt;</code> be about equality. So it would remain odd if, for instance, we rely on a defaulted <code class="language-cpp">&lt;=&gt;</code> whose return type is <code class="language-cpp">strong_equality</code> (which itself can never be used to determine actual equality).</p>
<p>We have two options here:</p>
<ol>
<li>
<p>Do nothing. Do not change the rules here at all, still require defaulted <code class="language-cpp">&lt;=&gt;</code> for use as a non-type template parameter. This means that there may be types which don't have a natural ordering for which we would have to both default <code class="language-cpp">==</code> and default <code class="language-cpp">&lt;=&gt;</code> (with <code class="language-cpp">strong_equality</code>), the latter being a function that <em>only</em> exists to opt-in to this behavior. </p>
</li>
<li>
<p>Change the definition of strong structural equality to use <code class="language-cpp">==</code> instead. The wording here would have to be slightly more complex: define a type <code class="language-cpp">T</code> as having strong structural equality if each subobject recursively has defaulted <code class="language-cpp">==</code> and none of the subobjects are floating point types. </p>
</li>
</ol>
<p>The impact of this change revolves around the code necessary to write a type that is intended to only be equality-comparable (not ordered) but also usable as a non-type template parameter: only <code class="language-cpp">operator==</code> would be necessary.</p>
<table style="width:100%">
<tr>
<th style="width:50%">
<p>Do nothing</p>
</th>
<th style="width:50%">
<p>Change definition</p>
</th>
</tr>
<tr>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">struct C {
    int i;
    bool operator==(C const&amp;) const = default;
    strong_equality operator&lt;=&gt;(C const&amp;) const = default;
};

template &lt;C x&gt;
struct Z { };</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">struct C {
    int i;
    bool operator==(C const&amp;) const = default;
};

template &lt;C x&gt;
struct Z { };</code></pre>
</td>
</tr>
</table>

<h3 id="change-defaulted-to-also-generate-a-defaulted">2.4. Change defaulted <code class="language-cpp">&lt;=&gt;</code> to also generate a defaulted <code class="language-cpp">==</code><a class="self-link" href="#change-defaulted-to-also-generate-a-defaulted"></a></h3>
<p>One of the important consequences of this proposal is that if you simply want lexicographic, member-wise, ordering for your type - you need to default <em>two</em> functions (<code class="language-cpp">==</code> and <code class="language-cpp">&lt;=&gt;</code>) instead of just one (<code class="language-cpp">&lt;=&gt;</code>):</p>
<table style="width:100%">
<tr>
<th style="width:50%">
<p>P0515/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">// all six
struct A {
    auto operator&lt;=&gt;(A const&amp;) const = default;
};

// just equality, no relational
struct B {
    strong_equality operator&lt;=&gt;(B const&amp;) const = default;
};</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px"><code class="language-cpp">// all six
struct A {
    bool operator==(A const&amp;) const = default;
    auto operator&lt;=&gt;(A const&amp;) const = default;
};

// just equality, no relational
struct B {
    bool operator==(B const&amp;) const = default;
};</code></pre>
</td>
</tr>
</table>

<p>Arguably, <code class="language-cpp">A</code> isn't terrible here and <code class="language-cpp">B</code> is somewhat simpler. But it makes this proposal seem like it's fighting against the promise of P0515 of making a trivial opt-in to ordering. </p>
<p>As an optional extension, this paper proposes that a defaulted <code class="language-cpp">&lt;=&gt;</code> operator also generate a defaulted <code class="language-cpp">==</code>. We can do this regardless of whether the return type of the defaulted <code class="language-cpp">&lt;=&gt;</code> is provided or not, since even <code class="language-cpp">weak_equality</code> implies <code class="language-cpp">==</code>.</p>
<p>This change, combined with the core proposal, means that one single defaulted operator is sufficient for full comparison. The difference is that, with this proposal, we still get optimal equality.</p>
<p>This change may also obviate the need for the previous optional extension of changing the definition of strong structural extension. But even still, the changes are worth considering separately. </p>
<h2 id="important-implications">3. Important implications<a class="self-link" href="#important-implications"></a></h2>
<p>This proposal means that for complex types (like containers), we have to write two functions instead of just <code class="language-cpp">&lt;=&gt;</code>. But we really have to do that anyway if we want performance. Even though the two <code class="language-cpp">vector</code> functions are very similar, and for <code class="language-cpp">optional</code> they are even more similar (see below), this seems like a very necessary change.</p>
<p>For compound types (like aggregates), depending on the preference of the previous choices, we either have to default to functions instead or still just default <code class="language-cpp">&lt;=&gt;</code>... but we get optimal performance. </p>
<p>Getting back to our initial example, we would write:</p>
<pre class="codehilite"><code class="language-cpp">struct S {
    vector&lt;string&gt; names;
    bool operator==(S const&amp;) const = default; // (*) if 2.4 not adopted
    auto operator&lt;=&gt;(S const&amp;) const = default;
};</code></pre>


<p>Even if we choose to require defaulting <code class="language-cpp">operator==</code> in this example, the fact that <code class="language-cpp">&lt;=&gt;</code> is no longer considered as a candidate for equality means that the worst case of forgetting this function is that equality <em>does not compile</em>. That is a substantial improvement over the alternative where equality compiles and has subtly worse performance that will be very difficult to catch.</p>
<h3 id="implications-for-types-that-have-special-but-not-different-comparisons">3.1. Implications for types that have special, but not different, comparisons<a class="self-link" href="#implications-for-types-that-have-special-but-not-different-comparisons"></a></h3>
<p>There are many kinds of types for which the defaulted comparison semantics are incorrect, but nevertheless don't have to do anything different between equality and ordering. One such example is <code class="language-cpp">optional&lt;T&gt;</code>. Having to write two functions here is extremely duplicative:</p>
<table style="width:100%">
<tr>
<th style="width:50%">
<p><a href="https://medium.com/@barryrevzin/implementing-the-spaceship-operator-for-optional-4de89fc6d5ec" title="Implementing the spaceship operator for optional">P0515/C++2a</a></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) const
    -&gt; decltype(compare_3way(*lhs, *rhs))
{
    if (lhs.has_value() &amp;&amp; rhs.has_value()) {
        return compare_3way(*lhs, *rhs);
    } else {
        return lhs.has_value() &lt;=&gt; rhs.has_value();
    }
}</code></pre>
</td>
<td>
<pre style="background:transparent;border:0px" data-line="4,7,9,16,19,21"><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) const
    -&gt; decltype(compare_3way(*lhs, *rhs))
{
    if (lhs.has_value() &amp;&amp; rhs.has_value()) {
        return compare_3way(*lhs, *rhs);
    } else {
        return lhs.has_value() &lt;=&gt; rhs.has_value();
    }
}

template &lt;typename T, typename U&gt;
constexpr auto operator==(optional&lt;T&gt; const&amp; lhs,
        optional&lt;U&gt; const&amp; rhs) const
    -&gt; decltype(*lhs == *rhs)
{
    if (lhs.has_value() &amp;&amp; rhs.has_value()) {
        return *lhs == *rhs;
    } else {
        return lhs.has_value() == rhs.has_value();
    }
}</code></pre>
</td>
</tr>
</table>

<p>As is probably obvious, the implementations of <code class="language-cpp">==</code> and <code class="language-cpp">&lt;=&gt;</code> are basically identical: the only difference is that <code class="language-cpp">==</code> calls <code class="language-cpp">==</code> and <code class="language-cpp">&lt;=&gt;</code> calls <code class="language-cpp">&lt;=&gt;</code> (or really <code class="language-cpp">compare_3way</code>). It may be very tempting to implement <code class="language-cpp">==</code> to just call <code class="language-cpp">&lt;=&gt;</code>, but that would be wrong! It's critical that <code class="language-cpp">==</code> call <code class="language-cpp">==</code> all the way down.</p>
<p>It's important to keep in mind three things.</p>
<ol>
<li>In C++17 we'd have to write six functions, so writing two is a large improvement. </li>
<li>These two functions may be duplicated, but they give us optimal performance - writing the one <code class="language-cpp">&lt;=&gt;</code> to generate all six comparison functions does not. </li>
<li>The amount of special types of this kind - types that have non-default comparison behavior but perform the same algorithm for both <code class="language-cpp">==</code> and <code class="language-cpp">&lt;=&gt;</code> - is fairly small. Most container types would have separate algorithms. Typical types default both, or just default <code class="language-cpp">==</code>. The canonical examples that would need special behavior are <code class="language-cpp">std::array</code> and <code class="language-cpp">std::forward_list</code> (which either have fixed or unknown size and thus cannot short-circuit) and <code class="language-cpp">std::optional</code> and <code class="language-cpp">std::variant</code> (which can't do default comparison). So this particular duplication is a fairly limited problem.</li>
</ol>
<h3 id="implications-for-comparison-categories">3.2. Implications for comparison categories<a class="self-link" href="#implications-for-comparison-categories"></a></h3>
<p>One of the features of P0515 is that you could default <code class="language-cpp">&lt;=&gt;</code> to, instead of returning an order, simply return some kind of equality:</p>
<pre class="codehilite"><code class="language-cpp">struct X {
    std::strong_equality operator&lt;=&gt;(X const&amp;) const = default;
};</code></pre>


<p>In a world where neither <code class="language-cpp">==</code> nor <code class="language-cpp">!=</code> would be generated from <code class="language-cpp">&lt;=&gt;</code>, this no longer makes much sense. We could have to require that the return type of <code class="language-cpp">&lt;=&gt;</code> be some kind of ordering - that is, at least <code class="language-cpp">std::partial_ordering</code>. Allowing the declaration of <code class="language-cpp">X</code> above would be misleading, at best. </p>
<p>This means there may not be a way to differentiate between <code class="language-cpp">std::strong_equality</code> and <code class="language-cpp">std::weak_equality</code>. The only other place to do this kind of differentiation would be if we somehow allowed it in the return of <code class="language-cpp">operator==</code>:</p>
<pre class="codehilite"><code class="language-cpp">struct X {
    std::strong_equality operator==(X const&amp;) const = default;
};</code></pre>


<p>And I'm not sure this makes any sense. </p>
<h2 id="wording">4. Wording<a class="self-link" href="#wording"></a></h2>
<p>What follows is the wording from the core sections of the proposal (2.1 and 2.2).</p>
<p>Change 10.10.3 [class.rel.eq] paragraph 2:</p>
<blockquote>
<p>The <ins>relational</ins> operator function with parameters <code class="language-cpp">x</code> and <code class="language-cpp">y</code> is defined as deleted if</p>
<ul>
<li>overload resolution ([over.match]), as applied to <code class="language-cpp">x &lt;=&gt; y</code> (also considering synthesized candidates with reversed order of parameters ([over.match.oper])), results in an ambiguity or a function that is deleted or inaccessible from the operator function, or</li>
<li>the operator <code class="language-cpp">@</code> cannot be applied to the return type of <code class="language-cpp">x &lt;=&gt; y</code> or <code class="language-cpp">y &lt;=&gt; x</code>.</li>
</ul>
<p>Otherwise, the operator function yields <code class="language-cpp">x &lt;=&gt; y @ 0</code> if an operator&lt;=&gt; with the original order of parameters was selected, or <code class="language-cpp">0 @ y &lt;=&gt; x</code> otherwise.</p>
</blockquote>
<p>Add a new paragraph after 10.10.3 [class.rel.eq] paragraph 2:</p>
<blockquote>
<p><ins>The return value <code class="language-cpp">V</code> of type <code class="language-cpp">bool</code> of the defaulted <code class="language-cpp">==</code> (equal to) operator function with parameters <code class="language-cpp">x</code> and <code class="language-cpp">y</code> of the same type is determined by comparing corresponding elements <code>x<sub>i</sub></code> and <code>y<sub>i</sub></code> in the expanded lists of subobjects ([class.spaceship]) for <code class="language-cpp">x</code> and <code class="language-cpp">y</code> until the first index <code class="language-cpp">i</code> where <code>x<sub>i</sub> == y<sub>i</sub></code> yields a value result which, contextually converted to bool, yields <code class="language-cpp">false</code>. If no such index exists, <code class="language-cpp">V</code> is <code class="language-cpp">true</code>. Otherwise, <code class="language-cpp">V</code> is <code class="language-cpp">false</code>.</ins></p>
</blockquote>
<p>Add another new paragraph after 10.10.3 [class.rel.eq] paragraph 2:</p>
<blockquote>
<p><ins>The <code class="language-cpp">!=</code> (not equal to) operator function with parameters <code class="language-cpp">x</code> and <code class="language-cpp">y</code> is defined as deleted if</ins></p>
<ul>
<li><ins>overload resolution ([over.match]), as applied to <code class="language-cpp">x == y</code> (also considering synthesized candidates with reversed order of parameters ([over.match.oper])), results in an ambiguity or a function that is deleted or inaccessible from the operator function, or</ins></li>
<li><ins>the negation operator cannot be applied to the return type of <code class="language-cpp">x == y</code> or <code class="language-cpp">y == x</code>.</ins></li>
</ul>
<p><ins>Otherwise, the <code class="language-cpp">!=</code> operator function yields <code class="language-cpp">!(x == y)</code> if an operator <code class="language-cpp">==</code> with the original order of parameters was selected, or <code class="language-cpp">!(y == x)</code> otherwise.</ins></p>
</blockquote>
<p>Change the example in [class.rel.eq] paragraph 3:</p>
<blockquote><pre><code class="language-cpp">struct C {
  friend std::strong_equality operator<=>(const C&, const C&);
  </code><code><del>friend bool operator==(const C& x, const C& y) = default; // OK, returns x <=> y == 0</del></code><code class="language-cpp">
  bool operator<(const C&) = default;                       // OK, function is deleted
  </code><code><ins>bool operator!=(const C&) = default;                      // OK, function is deleted</ins>
};

<ins>struct D {
  int i;
  friend bool operator==(const D& x, const D& y) const = default; // OK, returns x.i == y.i
  bool operator!=(const D& z) const = default;                    // OK, returns !(*this == z)
};</ins></code></pre></blockquote>

<p>Change 11.3.1.2 [over.match.oper] paragraph 3.4:</p>
<blockquote>
<p>For the relational ([expr.rel]) <del>and equality ([expr.eq])</del> operators, the rewritten candidates include all member, non-member, and built-in candidates for the operator <code class="language-cpp">&lt;=&gt;</code> for which the rewritten expression <code class="language-cpp">(x &lt;=&gt; y) @ 0</code> is well-formed using that operator <code class="language-cpp">&lt;=&gt;</code>. For the relational ([expr.rel])<del>, equality ([expr.eq]),</del> and three-way comparison ([expr.spaceship]) operators, the rewritten candidates also include a synthesized candidate, with the order of the two parameters reversed, for each member, non-member, and built-in candidate for the operator &lt;=&gt; for which the rewritten expression 0 @ (y &lt;=&gt; x) is well-formed using that operator&lt;=&gt;.  <ins>For the <code class="language-cpp">!=</code> (not equal to) operator ([expr.eq]), the rewritten candidates include all member, non-member, and built-in candidates for the operator <code class="language-cpp">==</code> for which the rewritten expression <code class="language-cpp">!(x == y)</code> is well-formed using that operator <code class="language-cpp">==</code>. For the equality operators, the rewritten candidates also include a synthesized candidate, with the order of the two parameters reversed, for each member, non-member, and built-in candidate for the operator <code class="language-cpp">==</code> for which the rewritten expression <code class="language-cpp">(y == x) @ true</code> is well-formed using that operator <code class="language-cpp">==</code>.</ins> <em>[ Note:</em> A candidate synthesized from a member candidate has its implicit object parameter as the second parameter, thus implicit conversions are considered for the first, but not for the second, parameter. <em>—end note]</em> In each case, rewritten candidates are not considered in the context of the rewritten expression. For all other operators, the rewritten candidate set is empty.</p>
</blockquote>
<h3 id="wording-for-redefining-strong-structural-equality">4.1. Wording for redefining strong structural equality<a class="self-link" href="#wording-for-redefining-strong-structural-equality"></a></h3>
<p>Replace 10.10.1 [class.compare.default] paragraph 2:</p>
<blockquote>
<p><del>A three-way comparison operator for a class type <code class="language-cpp">C</code> is a <em>structural comparison operator</em> if it is defined as defaulted in the definition of <code class="language-cpp">C</code>, and all three-way comparison operators it invokes are structural comparison operators. A type <code class="language-cpp">T</code> has <em>strong structural equality</em> if, for a glvalue <code class="language-cpp">x</code> of type <code class="language-cpp">const T</code>, <code class="language-cpp">x &lt;=&gt; x</code> is a valid expression of type <code class="language-cpp">std​::​strong_ordering</code> or <code class="language-cpp">std​::​strong_equality</code> and either does not invoke a three-way comparison operator or invokes a structural comparison operator.</del></p>
</blockquote>
<p>with:</p>
<blockquote>
<p><ins>An <code class="language-cpp">==</code> (equal to) operator is a <em>structural equality operator</em> if:</ins></p>
<ul>
<li><ins>it is a built-in candidate ([over.built]) where neither argument has floating point type, or</ins></li>
<li><ins>it is an operator for a class type <code class="language-cpp">C</code> that is defined as defaulted in the definition of <code class="language-cpp">C</code> and all <code class="language-cpp">==</code> operators it invokes are structural equality operators.</ins></li>
</ul>
<p><ins>A type <code class="language-cpp">T</code> has <em>strong structural equality</em> if, for a glvalue <code class="language-cpp">x</code> of type <code class="language-cpp">const T</code>, <code class="language-cpp">x == x</code> is a valid expression of type <code class="language-cpp">bool</code> and invokes a structural equality operator.</p>
</blockquote>
<h3 id="wording-for-defaulted-generating-a-defaulted">4.2. Wording for defaulted <code class="language-cpp">&lt;=&gt;</code> generating a defaulted <code class="language-cpp">==</code><a class="self-link" href="#wording-for-defaulted-generating-a-defaulted"></a></h3>
<p>Add to 10.10.3 [class.rel.eq], below the description of defaulted <code class="language-cpp">==</code>:</p>
<blockquote>
<p><ins>If the class definition does not explicitly declare an <code class="language-cpp">==</code> (equal to) operator function ([expr.eq]) and declares a defaulted three-way comparison operator function ([class.spaceship]) that is not defined as deleted, a defaulted <code class="language-cpp">==</code> operator function is declared <em>implicitly</em>. The implicitly-declared <code class="language-cpp">==</code> operator for a class <code class="language-cpp">X</code> will have the form</ins>  </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<ins><code>bool X::operator==(const X&amp;, const X&amp;)</code></ins>  </p>
<p><ins>and will follow the rules described above. </p>
</blockquote>
<h2 id="acknowledgements">5. Acknowledgements<a class="self-link" href="#acknowledgements"></a></h2>
<p>This paper most certainly would not exist without David Stone's extensive work in this area. Thanks also to Agustín Bergé for discussing issues with me.</p>
<h2 id="references">6. 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/2018/p0732r2.pdf">[P0732R2]</a><span style="margin-left: 5px;">"Class Types in Non-Type Template Parameters" by Jeff Snyder, Louis Dionne, 2018-06-06</span></li><li><a href="http://hackage.haskell.org/package/base-4.11.1.0/docs/Data-Eq.html">[haskell.eq]</a><span style="margin-left: 5px;">Data.Eq - Haskell documentation</span></li><li><a href="http://hackage.haskell.org/package/base-4.11.1.0/docs/Data-Ord.html">[haskell.ord]</a><span style="margin-left: 5px;">Data.Ord - Haskell documentation</span></li><li><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html">[kotlin.any]</a><span style="margin-left: 5px;">Any - Kotlin Programming Language</span></li><li><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-comparable/index.html">[kotlin.comp]</a><span style="margin-left: 5px;">Comparable - Kotlin Programming Language</span></li><li><a href="https://kotlinlang.org/docs/reference/operator-overloading.html#equals">[kotlin.oper]</a><span style="margin-left: 5px;">Operator overloading - Kotlin Programming Language</span></li><li><a href="https://medium.com/@barryrevzin/implementing-the-spaceship-operator-for-optional-4de89fc6d5ec">[revzin.impl]</a><span style="margin-left: 5px;">"Implementing the spaceship operator for optional" by Barry Revzin, 2017-11-16</span></li><li><a href="https://doc.rust-lang.org/src/core/slice/mod.rs.html?search=#4037-4053">[rust.eq]</a><span style="margin-left: 5px;">Implementation of Eq for Slice</span></li><li><a href="https://doc.rust-lang.org/reference/expressions/operator-expr.html#comparison-operators">[rust.oper]</a><span style="margin-left: 5px;">Comparison Operators - The Rust Reference</span></li><li><a href="https://doc.rust-lang.org/src/core/slice/mod.rs.html#4116-4136">[rust.ord]</a><span style="margin-left: 5px;">Implementation of Ord for Slice</span></li><li><a href="https://www.scala-lang.org/api/current/scala/Any.html#==(x$1:Any):Boolean">[scala.any]</a><span style="margin-left: 5px;">Scala Standard Library - Any</span></li><li><a href="https://www.scala-lang.org/api/current/scala/math/Ordered.html">[scala.ord]</a><span style="margin-left: 5px;">Scala Standard Library - Ordered</span></li><li><a href="http://stepanovpapers.com/DeSt98.pdf">[stepanov.fogp]</a><span style="margin-left: 5px;">"Fundamentals of Generic Programming" by James C. Dehnert and Alexander Stepanov, 1998</span></li><li><a href="https://github.com/davidstone/isocpp/blob/b2db8e00dfec04a7742c67a5ea6e9575c9aba03d/operator-spaceship/I-did-not-order-this.md">[stone.order]</a><span style="margin-left: 5px;">"I did not order this! Why is it on my bill?" by David Stone, 2018-08-06</span></li><li><a href="https://developer.apple.com/documentation/swift/comparable">[swift.comp]</a><span style="margin-left: 5px;">Comparable - Swift Standard Library</span></li><li><a href="https://developer.apple.com/documentation/swift/equatable">[swift.eq]</a><span style="margin-left: 5px;">Equatable - Swift Standard Library</span></li></ul>
</html>