<!DOCTYPE html>
<html>

<head>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>P0726R0: Does the Concepts TS Improve on C++17?</title>


<style type="text/css">
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; }

ul :first-child, ol :first-child {
  margin-top: 0; }

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; }

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;
  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;
}
@media screen and (min-width: 914px) {
    body {
        width: 854px;
        margin:0 auto;
    }
}
@media print {
	table, pre {
		page-break-inside: avoid;
	}
	pre {
		word-wrap: break-word;
	}
}
</style>

<style type="text/css">
/**
 * prism.js default theme for JavaScript, CSS and HTML
 * Based on dabblet (http://dabblet.com)
 * @author Lea Verou
 */

code[class*="language-"],
pre[class*="language-"] {
	color: black;
	background: none;
	text-shadow: 0 1px white;
	font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
	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: #f5f2f0;
}

/* 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: #a67f59;
	background: hsla(0, 0%, 100%, .5);
}

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

.token.function {
	color: #DD4A68;
}

.token.regex,
.token.important,
.token.variable {
	color: #e90;
}

.token.important,
.token.bold {
	font-weight: bold;
}
.token.italic {
	font-style: italic;
}

.token.entity {
	cursor: help;
}
</style>


</head>

<body>

<div align="right" style="font-style: italic">
Document number: P0726R0
<br/>
Audience: EWG
<br/>
<br/>
<a href="mailto:dexonsmith@apple.com">Duncan P. N. Exon Smith</a><br/>
2017-07-07<br/>
</div>
<hr/>

<h1 id="toc_0">Does the Concepts TS Improve on C++17?</h1>

<p>One of the primary goals of the <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4674.pdf">C++ Concepts TS</a> is to provide better template error messages. Using the reference implementations provided by <a href="https://gcc.gnu.org/gcc-7/">GCC 7.1&#39;s Concepts</a> and <a href="https://github.com/CaseyCarter/cmcstl2">concept-enabled Ranges</a>, this paper evaluates the error messages for a few simple STL use cases. The results are surprising, and not encouraging.</p>

<p>The Concepts TS is also intended to simplify Generic Programming, but in practice, concept requirements are far from simple. The <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4671.pdf">Ranges TS</a>, whose concepts depend on metaprogramming techniques and other arcana, demonstrates the challenges of making concepts work in the real world.</p>

<p>Finally, C++17 already has the tools to express requirements on
templates, check valid syntax, and compose those checks into reusable
entities, providing most of the features of concepts without new
language extensions. The Concepts TS adds significant complexity and
little value to C++17.  Until demonstrable benefits outweigh the costs, it
should not be merged.</p>

<h2>Contents</h2>

<ul>
<li><a href="#errors">Error Messages with Concepts and Ranges</a>
<ul>
  <li><a href="#more-sorting">More sorting</a></li>
</ul>
</li>
<li><a href="#generic-programming">Generic Programming with Concepts</a>
<ul>
  <li><a href="#real-world">Real-world concepts</a></li>
</ul>
</li>
<li><a href="#tools">C++17 Already Has the Tools for Generic Programming</a>
<ul>
  <li><a href="#requires-expressions">Requires expressions</a></li>
  <li><a href="#concept-based-overloading">Concept-based overloading</a></li>
</ul>
</li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>



<a name="errors"></a>
<h2 id="toc_1">Error Messages with Concepts and Ranges</h2>

<p>Using <a href="https://gcc.gnu.org/gcc-7/">GCC 7.1&#39;s implementation of Concepts</a> and <a href="https://github.com/CaseyCarter/cmcstl2">Casey Carter&#39;s reference implementation of the Ranges TS</a>, let&#39;s take a canonical example (drawn from <a href="https://en.wikipedia.org/wiki/Concepts_(C%2B%2B)#Compiler_diagnostics">here</a> but used everywhere) where the <code>sort</code> algorithm is called on a <code>list</code>:</p>

<div><pre><code class="language-cpp">std::list&lt;int&gt; l = {2, 1, 3};
std::experimental::ranges::sort(l.begin(), l.end());</code></pre></div>

<p>The complete diagnostic is 99 lines, with 35 notes, and is available in <a href="https://gist.github.com/dexonsmith/040912b08b6b2ee8c203bfc3f003acc4#file-stl2_concepts_sort_list-txt">this gist</a>. It begins by noting the <code>sort</code> candidate that failed:</p>

<div><pre><code class="language-none">note: candidate: I std::experimental::ranges::v1::sort(I, S, Comp, Proj) [with I = std::_List_iterator&lt;int&gt;; S = std::_List_iterator&lt;int&gt;; Comp = std::experimental::ranges::v1::less&lt;void&gt;; Proj = std::experimental::ranges::v1::identity]
  I sort(I first, S sent, Comp comp = Comp{}, Proj proj = Proj{})</code></pre></div>

<p>and that the constraints of this candidate are unsatisfied:</p>

<div><pre><code class="language-none">note:   constraints not satisfied</code></pre></div>

<p>Next is a note that indicates it was the <code>RandomAccessIterator</code> requirement that failed:</p>

<div><pre><code class="language-none">note: within ‘template&lt;class I&gt; concept const bool std::experimental::ranges::v1::RandomAccessIterator&lt;I&gt; [with I = std::_List_iterator&lt;int&gt;]’ </code></pre></div>

<p>Anyone that understands the concepts involved can stop here. However, the diagnostic continues for another 30+ notes, highlighting various syntactic failures, e.g., regarding iterator tags:</p>

<div><pre><code class="language-none">note: within ‘template&lt;class T, class U&gt; concept const bool std::experimental::ranges::v1::DerivedFrom&lt;T, U&gt; 
note: ‘std::experimental::ranges::v1::random_access_iterator_tag’ is not a base of ‘std::experimental::ranges::v1::bidirectional_iterator_tag’</code></pre></div>

<a name="more-sorting"></a>
<h3 id="toc_2">More sorting</h3>

<p>Let&#39;s consider a similar example (<a href="https://gist.github.com/dexonsmith/040912b08b6b2ee8c203bfc3f003acc4#file-stl2_concepts_sort_vector_notcomparable-txt">gist here</a>):</p>

<div><pre><code class="language-cpp">void f2(std::vector&lt;MyType&gt; &amp;v) {
  std::experimental::ranges::sort(v.begin(), v.end());
}</code></pre></div>

<p>Here, GCC again identifies the candidate template and that the constraints are unsatisfied:</p>

<div><pre><code class="language-none">note: candidate: I std::experimental::ranges::v1::sort(I, S, Comp, Proj) [with I = __gnu_cxx::__normal_iterator&lt;MyType*, std::vector&lt;MyType&gt; &gt;; S = __gnu_cxx::__normal_iterator&lt;MyType*, std::vector&lt;MyType&gt; &gt;; Comp = std::experimental::ranges::v1::less&lt;void&gt;; Proj = std::experimental::ranges::v1::identity]
  I sort(I first, S sent, Comp comp = Comp{}, Proj proj = Proj{})
    ^~~~
include/stl2/detail/algorithm/sort.hpp:31:4: note:   constraints not satisfied</code></pre></div>

<p>followed by the concept constraint that failed:</p>

<div><pre><code class="language-none">note: within ‘template&lt;class I, class R, class P&gt; concept const bool std::experimental::ranges::v1::Sortable&lt;I, R, P&gt;</code></pre></div>

<p><code>Sortable</code> doesn&#39;t tell us enough about what went wrong, so we follow the sequence of notes:</p>

<div><pre><code class="language-none">note: within ‘template&lt;class F, class I1, class I2&gt; concept const bool std::experimental::ranges::v1::IndirectStrictWeakOrder&lt;F, I1, I2&gt;
note: within ‘template&lt;class R, class T, class U&gt; concept const bool std::experimental::ranges::v1::StrictWeakOrder&lt;R, T, U&gt;

.
. (skipped 29 notes)
.

note: ... and 5 more constraint errors not shown</code></pre></div>

<p>The actual problem is that <code>MyType</code> didn&#39;t provide an operator <code>&lt;</code>, which didn&#39;t show up in any of the 85+ lines of error messages produced by GCC. </p>

<p>One of the main selling points of the Concepts TS is improved template error messages, but experience with simple STL examples shows quite the opposite: the diagnostics are verbose, dumping a deep backtrace of concept references, while not providing critical information about what went wrong to the user. In fact, the error message provided by the same compiler in C++17 mode is actually better than what is provided by concepts, because it almost immediately states the lack of <code>&lt;</code>:</p>

<div><pre><code class="language-none">/opt/gcc/include/c++/7.1.0/bits/predefined_ops.h:43:23: error: no match for ‘operator&lt;’ (operand types are ‘MyType’ and ‘MyType’)
       { return *__it1 &lt; *__it2; }
                ~~~~~~~^~~~~~~~</code></pre></div>

<a name="generic-programming"></a>
<h2 id="toc_3">Generic Programming with Concepts</h2>

<p>Let&#39;s look at concepts from the other side, and add requirements to an existing generic algorithm.  Sticking with <code>sort</code>, its most prominent requirement is the need for random access iterators:</p>

<div><pre><code class="language-cpp">template &lt;class Iter&gt;
  requires RandomAccessIterator&lt;Iter&gt;
void sort(Iter first, Iter last) { ... }</code></pre></div>

<p>This is a straightforward change, and will trigger a concept-based diagnostic if the user calls us with <code>list</code> iterators. However, users will find that it&#39;s insufficient: sorting a container with a type that lacks <code>&lt;</code> will still produce a diagnostic from inside the template&#39;s implementation. Fortunately, that&#39;s easy to fix with another requirement:</p>

<div><pre><code class="language-cpp">template &lt;class Iter&gt;
  requires RandomAccessIterator&lt;Iter&gt; &amp;&amp; StrictWeakOrder&lt;value_type_t&lt;Iter&gt;&gt;
void sort(Iter first, Iter last) { ... }</code></pre></div>

<p>That&#39;s closer, but users will eventually discover that we still haven&#39;t covered <code>const</code>:</p>

<div><pre><code class="language-cpp">void f(const std::vector&lt;int&gt; &amp;v) {
  sort(v.begin(), v.end()); // traditional template backtrace
}</code></pre></div>

<p>Perhaps this is a natural back-and-forth when adopting concepts, but where does it lead?  Do concepts make generic code simpler to write?</p>

<a name="real-world"></a>
<h3 id="toc_4">Real-world concepts</h3>

<p>To find out, let&#39;s go back to the Ranges TS for the requirements on <code>sort</code>:</p>

<div><pre><code class="language-cpp">template &lt;RandomAccessIterator I, Sentinel&lt;I&gt; S, class Comp = less&lt;&gt;,
    class Proj = identity&gt;
  requires Sortable&lt;I, Comp, Proj&gt;()
  I sort(I first, S last, Comp comp = Comp{}, Proj proj = Proj{});</code></pre></div>

<p>This looks simple, but somebody actually had to write <code>Sortable</code>. Let&#39;s take a look at what&#39;s involved:</p>

<div><pre><code class="language-cpp">template &lt;class I, class R = less&lt;&gt;, class P = identity&gt;
  concept bool Sortable() {
    return Permutable&lt;I&gt;() &amp;&amp;
           IndirectStrictWeakOrder&lt;R, projected&lt;I, P&gt;&gt;();
}

template &lt;class I&gt; concept bool Permutable() { ... }

template &lt;class F, class I1, class I2 = I1&gt;
  concept bool IndirectStrictWeakOrder() {
    return Readable&lt;I1&gt;() &amp;&amp; Readable&lt;I2&gt;() &amp;&amp;
           CopyConstructible&lt;F&gt;() &amp;&amp;
           StrictWeakOrder&lt;F&amp;, value_type_t&lt;I1&gt;&amp;, value_type_t&lt;I2&gt;&amp;&gt;() &amp;&amp;
           StrictWeakOrder&lt;F&amp;, value_type_t&lt;I1&gt;&amp;, reference_t&lt;I2&gt;&gt;() &amp;&amp;
           StrictWeakOrder&lt;F&amp;, reference_t&lt;I1&gt;, value_type_t&lt;I2&gt;&amp;&gt;() &amp;&amp;
           StrictWeakOrder&lt;F&amp;, reference_t&lt;I1&gt;, reference_t&lt;I2&gt;&gt;() &amp;&amp;
           StrictWeakOrder&lt;F&amp;, iter_common_reference_t&lt;I1&gt;, iter_common_reference_t&lt;I2&gt;&gt;();
  }</code></pre></div>

<p>By the time we get to a lower-level concept such as <code>IndirectStrictWeakOrder</code>, we see the complexity required to express simple notions correctly. Still, we haven&#39;t gotten to the key constraint that allows ordering, so we&#39;ll keep going:</p>

<div><pre><code class="language-cpp">template &lt;class R, class T, class U&gt;
concept bool StrictWeakOrder() {
  return Relation&lt;R, T, U&gt;();
}

template &lt;class R, class T&gt;
concept bool Relation() {
  return Predicate&lt;R, T, T&gt;();
}

template &lt;class R, class T, class U&gt;
concept bool Relation() {
  return Relation&lt;R, T&gt;() &amp;&amp;
    Relation&lt;R, U&gt;() &amp;&amp;
    CommonReference&lt;const T&amp;, const U&amp;&gt;() &amp;&amp;
    Relation&lt;R,
      common_reference_t&lt;const T&amp;, const U&amp;&gt;&gt;() &amp;&amp;
    Predicate&lt;R, T, U&gt;() &amp;&amp;
    Predicate&lt;R, U, T&gt;();
}

template &lt;class F, class... Args&gt;
concept bool Predicate() {
  return RegularInvocable&lt;F, Args...&gt;() &amp;&amp;
    Boolean&lt;result_of_t&lt;F&amp;&amp;(Args&amp;&amp;...)&gt;&gt;();
}</code></pre></div>

<p>Here, we see what is effectively a syntactic requirement: the result of calling <code>F</code> with the given arguments (<code>Args</code>) must satisfy the <code>Boolean</code> concept. <code>Boolean</code> is specified as:</p>

<div><pre><code class="language-cpp">template &lt;class B&gt;
concept bool Boolean() {
  return MoveConstructible&lt;B&gt;() &amp;&amp; 
    requires(const B b1, const B b2, const bool a) {
      bool(b1);
      { b1 } -&gt; bool;
      bool(!b1);
      { !b1 } -&gt; bool;
      { b1 &amp;&amp; b2 } -&gt; Same&lt;bool&gt;;
      { b1 &amp;&amp; a } -&gt; Same&lt;bool&gt;;
      { a &amp;&amp; b1 } -&gt; Same&lt;bool&gt;;
      { b1 || b2 } -&gt; Same&lt;bool&gt;;
      { b1 || a } -&gt; Same&lt;bool&gt;;
      { a || b1 } -&gt; Same&lt;bool&gt;;
      { b1 == b2 } -&gt; bool;
      { b1 != b2 } -&gt; bool;
      { b1 == a } -&gt; bool;
      { a == b1 } -&gt; bool;
      { b1 != a } -&gt; bool;
      { a != b1 } -&gt; bool;
  };
}</code></pre></div>

<p>This is quite a large concept, because it is trying to model all of the different ways in which a <code>Boolean</code> type can be correctly used. Interestingly, some of the requirements of <code>Boolean</code> constrain the result to <code>Same&lt;bool&gt;</code> (to mean &quot;exactly <code>bool</code>&quot;) while others use <code>bool</code> (to mean &quot;implicitly convertible to <code>bool</code>&quot;). The distinction is subtle, requiring fairly deep knowledge of C++.</p>

<p>One can imagine a simple definition for <code>Sortable</code>, but the Ranges TS illustrates that simple concepts aren&#39;t sufficient: to actually capture the intended behavior of a C++ template, one needs to expose all of the complexity of the language. The concepts above are not simple to understand, even for an expert, and they&#39;re not unique in that regard: browsing through the Ranges TS, many of the concepts have extremely complicated definitions with subtle uses of <code>Same</code>, <code>reference_t</code>, and helper concepts like <code>Boolean</code> and <code>CommonReference</code>.  Writing new concepts requires deep knowledge of not only the new features in the Concepts TS, but of several other difficult parts of C++, including overload resolution and implicit conversions.</p>

<a name="tools"></a>
<h2 id="toc_5">C++17 Already Has the Tools for Generic Programming</h2>

<p>The Concepts TS is providing tools for generic programming, but those tools already exist in C++. For example, <code>std::enable_if</code> expresses the requirements of a template. Let&#39;s turn that <code>sort</code> example into valid C++17:</p>

<div><pre><code class="language-cpp">template &lt;class Iter, class = std::enable_if_t&lt;
    RandomAccessIterator&lt;Iter&gt; &amp;&amp; LessThanComparable&lt;value_type_t&lt;Iter&gt;&gt;&gt;&gt;
void sort(first, Iter last) { ... }</code></pre></div>

<p>The &quot;concepts&quot; here are <code>constexpr</code> variable templates using existing facilities. A simple form of <code>RandomAccessIerator</code> checks the iterator category (the iterator category is still used for this purpose in the Ranges TS):</p>

<div><pre><code class="language-cpp">template &lt;class Iter&gt;
constexpr bool RandomAccessIterator =
    std::is_convertible&lt;iterator_category_t&lt;Iter&gt;,
                        std::random_access_iterator_tag&gt;::value;</code></pre></div>

<p>and <code>LessThanComparable</code> uses existing techniques for checking valid expressions:</p>

<div><pre><code class="language-cpp">template &lt;class T, class = void&gt;
constexpr bool LessThanComparable = false;

template &lt;class T&gt;
constexpr bool LessThanComparable&lt;T, std::void_t&lt;
    decltype(static_cast&lt;bool&gt;(std::declval&lt;T&gt;() &lt; std::declval&lt;T&gt;()))&gt;&gt; = true;</code></pre></div>

<p>C++ template libraries already use these techniques to tate their template requirements. For example, <a href="https://github.com/ericniebler/range-v3">ranges-v3</a>, which is a C++11/14 implementation for the Ranges TS, makes extensive use of <code>enable_if</code> and concepts-like checking.</p>

<a name="requires-expressions"></a>
<h3 id="toc_6">Requires expressions</h3>

<p>The syntactic checks of the previous section were drastically simplified. However, more involved syntactic checks on par with the Ranges TS can be implemented already in C++17. For example, here is a C++17 definition of the <code>StrictTotallyOrdered</code> concept from the Ranges TS: </p>

<div><pre><code class="language-cpp">template &lt;class T, class U = T&gt;
constexpr bool StrictTotallyOrdered =
    CommonReference&lt;const T&amp;, const U&amp;&gt; &amp;&amp;
    StrictTotallyOrdered&lt;T&gt; &amp;&amp;
    StrictTotallyOrdered&lt;U&gt; &amp;&amp;
    StrictTotallyOrdered&lt;
        std::remove_cv_t&lt;std::remove_reference_t&lt;
            std::common_reference_or_void_t&lt;const T&amp;, const U&amp;&gt;&gt;&gt;&gt;() &amp;&amp;
    EqualityComparable&lt;T, U&gt; &amp;&amp;
    requires&lt;const T, const U&gt;([](auto&amp;&amp; t, auto&amp;&amp; u) -&gt; std::enable_if_t&lt;
        Boolean&lt;decltype(t &lt; u)&gt; &amp;&amp;
        Boolean&lt;decltype(t &gt; u)&gt; &amp;&amp;
        Boolean&lt;decltype(t &lt;= u)&gt; &amp;&amp;
        Boolean&lt;decltype(t &gt;= u)&gt; &amp;&amp;
        Boolean&lt;decltype(u &lt; t)&gt; &amp;&amp;
        Boolean&lt;decltype(u &gt; t)&gt; &amp;&amp;
        Boolean&lt;decltype(u &lt;= t)&gt; &amp;&amp;
        Boolean&lt;decltype(u &gt;= t)&gt;&gt; {});

template &lt;class T&gt;
constexpr bool StrictTotallyOrdered&lt;T&gt; =
    EqualityComparable&lt;T&gt; &amp;&amp;
    requires&lt;const T, const T&gt;([](auto&amp;&amp; a, auto&amp;&amp; b) -&gt; std::enable_if_t&lt;
        Boolean&lt;decltype(a &lt; b)&gt; &amp;&amp;
        Boolean&lt;decltype(a &gt; b)&gt; &amp;&amp;
        Boolean&lt;decltype(a &lt;= b)&gt; &amp;&amp;
        Boolean&lt;decltype(a &gt;= b)&gt;&gt; {});</code></pre></div>

<p>This defines the concepts <code>StrictTotallyOrdered&lt;T, U&gt;</code> and
<code>StrictTotallyOrdered&lt;T&gt;</code> as <code>constexpr bool</code> variables, relying on
previously-defined concepts <a href="http://en.cppreference.com/w/cpp/experimental/ranges">as described at
cppreference</a> and <a href="https://gist.github.com/dexonsmith/509d31e0960d45958035b2bffd37bfc5">demoed
in this gist</a>.</p>

<p>Focusing on the single-argument case, <code>StrictTotallyOrdered&lt;T&gt;</code> refines
<code>EqualityComparable&lt;T&gt;</code>, and requires that the return type of the relational
operators on <code>const T</code> conform to the <code>Boolean</code> concept.</p>

<p>The syntactic logic is handled by the <code>requires</code> function (inspired by
<a href="http://boostorg.github.io/hana/index.html#tutorial-introspection-is_valid">hana::is_valid</a>,
and not so different from
<a href="http://en.cppreference.com/w/cpp/experimental/is_detected">std::experimental::is_detected</a>).
<code>requires</code> takes a type list as template parameters and a generic lambda as an
argument, and returns <code>true</code> iff the lambda can be called with that type list.
The return type of the lambda is a playground for testing arbitrary C++ syntax
inside <code>decltype</code>, and here was used to check for concept conformance.</p>

<p>Here are a few examples to demonstrate its use more broadly:</p>

<div><pre><code class="language-cpp">// T has operator++
requires&lt;T&gt;([](auto&amp;&amp; x) -&gt; decltype(++x) {})

// T has operator++ and return type is T&amp;
requires&lt;T&gt;([](auto&amp;&amp; x) -&gt; std::enable_if_t&lt;Same&lt;decltype(++x), T&amp;&gt;&gt; {})
requires&lt;T&gt;([](auto&amp;&amp; x) -&gt; std::enable_if_t&lt;
    std::is_same_v&lt;decltype(++x), T&amp;&gt;&gt; {}) // equivalent

// A bool can be constructed from const T
requires&lt;const T&gt;([](auto&amp;&amp; x) -&gt; decltype(bool(x))) {})
requires&lt;const T&gt;([](auto&amp;&amp; x) -&gt; std::enable_if_t&lt;
    std::is_constructible_v&lt;bool, decltype(x))&gt;&gt; {}) // equivalent

// Return type of operator&amp;&amp; on const T is a Boolean
requires&lt;const T&gt;([](auto&amp;&amp; x) -&gt; std::enable_if_t&lt;
    Boolean&lt;decltype(x &amp;&amp; x)&gt;&gt; {})

// Return type of operator== on const T is implicitly convertible to bool
requires&lt;const T&gt;([](auto&amp;&amp; x) -&gt; std::enable_if_t&lt;
    ConvertibleTo&lt;decltype(x == x), bool&gt;&gt; {})

// Return type of operator! on const T is bool.
requires&lt;const T&gt;([](auto&amp;&amp; x) -&gt; std::enable_if_t&lt;
    Same&lt;decltype(!x), bool&gt;&gt; {})

// Return type of operator+= on T and const U is T&amp;.
requires&lt;T, const U&gt;([](auto&amp;&amp; t, auto&amp;&amp; u) -&gt; std::enable_if_t&lt;
    Same&lt;decltype(t += u), T&amp;&gt;&gt; {})</code></pre></div>

<p>The reference implementation is straightforward.</p>

<div><pre><code class="language-cpp">namespace detail {
template &lt;class F, class... Args,
          class = decltype(std::declval&lt;F&amp;&amp;&gt;()(std::declval&lt;Args&amp;&amp;&gt;()...))&gt;
constexpr bool requires_impl(int) { return true; }
template &lt;class F, class... Args&gt;
constexpr bool requires_impl(...) { return false; }
} // namespace detail

template &lt;class... Args, class F&gt;
constexpr bool requires(F&amp;&amp;) {
  return detail::requires_impl&lt;F&amp;&amp;, Args&amp;&amp;...&gt;(int{});
}</code></pre></div>

<a name="concept-based-overloading"></a>
<h3 id="toc_7">Concept-based overloading</h3>

<p>Another feature of the Concepts TS is concept-based overloading, which allows multiple versions of the same template that differ only based on their requirements. A typical example is <code>std::advance</code>:</p>

<div><pre><code class="language-cpp">template &lt;class Iter&gt;
  requires InputIterator&lt;Iter&gt;
void advance(Iter &amp;i, difference_type_t&lt;Iter&gt; n) {
  while (n &gt; 0) { ++i; --n; }
}

template &lt;class Iter&gt;
  requires BidirectionalIterator&lt;Iter&gt;
void advance(Iter &amp;i, difference_type_t&lt;Iter&gt; n) {
  while (n &lt; 0) { --i; ++n; }
  while (n &gt; 0) { ++i; --n; }
}

template &lt;class Iter&gt;
  requires RandomAccessIterator&lt;Iter&gt;
void advance(Iter &amp;i, difference_type_t&lt;Iter&gt; n) {
  i += n;
}</code></pre></div>

<p>A call to <code>advance</code> will pick the most specialized algorithm. C++17 provides a more concise, direct way of accomplishing the same thing via constexpr-if:</p>

<div><pre><code class="language-cpp">template &lt;class Iter, class = std::enable_if_t&lt;
    InputIterator&lt;Iter&gt;&gt;&gt;
void advance(Iter &amp;i, difference_type_t&lt;Iter&gt; n) {
  if constexpr (RandomAccessIterator&lt;Iter&gt;) {
    i += n;
  } else if constexpr (BidirectionalIterator&lt;Iter&gt;) {
    while (n &lt; 0) { --i; ++n; }
    while (n &gt; 0) { ++i; --n; }
  } else {
    while (n &gt; 0) { ++i; --n; }
  }
}</code></pre></div>

<p>The <code>if constexpr</code> formulation is simpler than the one using concept-based overloading. It only presents a single operation to the user via a single signature, which better matches the intent: there is <em>one</em> <code>advance</code> operation provided, and it happens that it will provide more efficient behavior with some types. The implementation is also much simpler, 
because it reads like normal executable code with easy-to-understand control flow.</p>

<p>There are some (rare) cases where a third-party might want to introduce a different implementation based on new concepts, but these can be addressed by other <a href="http://www.boost.org/community/generic_programming.html#tag_dispatching">well-known techniques</a> for overloading. The vast majority of uses of concept-based overloading are better addressed via constexpr-if.</p>

<a name="conclusion"></a>
<h2 id="toc_8">Conclusion</h2>

<p>The introduction of new features can simplify the C++ language, and <code>constexpr</code> is a fine example. Before <code>constexpr</code>, computing constant values involved arcane template metaprogramming that baffled many users. <code>constexpr</code> made such computations drastically simpler, by extended the capabilities of otherwise-ordinary functions in intuitive ways, opening up compile-time computation to a much larger audience. Similarly, lambdas opened up functional programming idioms to a far larger audience by making function objects more convenient and ubiquitous.</p>

<p>The Concepts TS does not simplify Generic Programming. The experience with GCC and the reference implementation of the Ranges TS raises serious concerns about whether they can improve the experience of using template libraries. The Ranges TS itself illustrates how the language complexity leaks through concepts and into the user experience, and demonstrates that real-world concepts are not simple or easy to write or use.</p>

<p>In the 14 years since this approach to concept support was first
proposed, C++ has evolved significantly, with the introduction of
constexpr, variable templates, constexpr-if, the expansion of SFINAE,
and the invention of numerous new template techniques, which together
provide the tools that developers are already using
to express the ideas of generic programming. Any language feature
added to support these idioms should significantly improve on the
results we can get today.  </p>

<p>The Concepts TS does not appear to do so.</p>




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

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

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

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


</body>

</html>
