595 lines
81 KiB
HTML
595 lines
81 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh" dir="ltr" class="client-nojs" xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh">
|
|
<head><meta http-equiv="x-ua-compatible" content="ie=edge">
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
<title>约束与概念 (C++20 起)</title>
|
|
<link rel="stylesheet" href="ext.css" />
|
|
<link rel="stylesheet" href="site_modules.css" />
|
|
</head>
|
|
<body class="mediawiki ltr sitedir-ltr ns-0 ns-subject page-cpp_language_constraints skin-cppreference2 action-view cpp-navbar">
|
|
<div id="cpp-content-base">
|
|
<div id="content"><a id="top"></a>
|
|
<h1 id="firstHeading" class="firstHeading">约束与概念 <span class="t-mark-rev t-since-cxx20">(C++20 起)</span></h1>
|
|
<div id="bodyContent">
|
|
<div id="contentSub"><span class="subpages">< <a href="cpp.html">cpp</a>‎ | <a href="cpp-language.html">language</a></span></div>
|
|
<div id="mw-content-text" lang="zh" dir="ltr" class="mw-content-ltr" xml:lang="zh">
|
|
<dl>
|
|
<dd><i>本页面描述了 C++20 接纳的核心语言特性。对于标准库中使用的具名类型要求,见<a href="cpp-named_req.html">具名要求</a>。有关这个功能特性的概念 TS 版本,见<a href="cpp-experimental-constraints.html">此处</a>。</i></dd>
|
|
</dl>
|
|
<p><a href="cpp-language-class_template.html">类模板</a>,<a href="cpp-language-function_template.html">函数模板</a>,以及非模板函数(通常是类模板的成员),可以关联到<i>约束(constraint)</i>,它指定了对模板实参的一些要求,这些要求可以被用于选择最恰当的函数重载和模板特化。</p>
|
|
<p>这种要求的具名集合被称为<i>概念(concept)</i>。每个概念都是谓词,在编译时求值,并在自己被用作约束时成为模板接口的一部分:</p>
|
|
<div class="t-example">
|
|
<div class="t-example-live-link"></div>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co2">#include <string></span>
|
|
<span class="co2">#include <cstddef></span>
|
|
<span class="co2">#include <concepts></span>
|
|
|
|
<span class="co1">// 概念 "Hashable" 的声明可以被符合以下条件的任意类型 T 满足:</span>
|
|
<span class="co1">// 对于 T 类型的值 a,表达式 std::hash<T>{}(a) 可以编译并且它的结果可以转换到 std::size_t</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
concept Hashable <span class="sy1">=</span> requires<span class="br0">(</span>T a<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="br0">{</span> <a href="cpp-utility-hash.html"><span class="kw1124">std::<span class="me2">hash</span></span></a><span class="sy1"><</span>T<span class="sy1">></span><span class="br0">{</span><span class="br0">}</span><span class="br0">(</span>a<span class="br0">)</span> <span class="br0">}</span> <span class="sy2">-</span><span class="sy1">></span> std<span class="sy4">::</span><span class="me2">convertible_to</span><span class="sy1"><</span><a href="cpp-types-size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a><span class="sy1">></span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">struct</span> meow <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>Hashable T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 受约束的 C++20 函数模板</span>
|
|
|
|
<span class="co1">// 应用相同约束的另一种方式:</span>
|
|
<span class="co1">// template<typename T></span>
|
|
<span class="co1">// requires Hashable<T></span>
|
|
<span class="co1">// void f(T); </span>
|
|
<span class="co1">// </span>
|
|
<span class="co1">// template<typename T></span>
|
|
<span class="co1">// void f(T) requires Hashable<T>; </span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw1">using</span> std<span class="sy4">::</span><span class="me2">operator</span><span class="st0">""</span>s<span class="sy4">;</span>
|
|
|
|
f<span class="br0">(</span><span class="st0">"abc"</span>s<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK,std::string 满足 Hashable</span>
|
|
f<span class="br0">(</span>meow<span class="br0">{</span><span class="br0">}</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:meow 不满足 Hashable</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
</div>
|
|
<p>在编译时(模板实例化过程的早期)就检测是否违背约束,这样错误信息就更容易理解:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><a href="cpp-container-list.html"><span class="kw1275">std::<span class="me2">list</span></span></a><span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span> l <span class="sy1">=</span> <span class="br0">{</span><span class="nu0">3</span>,<span class="sy2">-</span><span class="nu0">1</span>,<span class="nu0">10</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<a href="cpp-algorithm-sort.html"><span class="kw1618">std::<span class="me2">sort</span></span></a><span class="br0">(</span>l.<span class="me1">begin</span><span class="br0">(</span><span class="br0">)</span>, l.<span class="me1">end</span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="co1">// 没有概念时典型编译器的诊断:</span>
|
|
<span class="co1">// 二元表达式的操作数非法 ('std::_List_iterator<int>' 和 'std::_List_iterator<int>')</span>
|
|
<span class="co1">// std::__lg(__last - __first) * 2);</span>
|
|
<span class="co1">// ~~~~~~ ^ ~~~~~~~</span>
|
|
<span class="co1">// …… 50 行输出……</span>
|
|
<span class="co1">//</span>
|
|
<span class="co1">// 有概念时典型编译器的诊断:</span>
|
|
<span class="co1">// 错误:无法以 std::_List_iterator<int> 调用 std::sort</span>
|
|
<span class="co1">// 注意:未满足概念 RandomAccessIterator<std::_List_iterator<int>></span></pre></div>
|
|
</div>
|
|
<p>概念的目的是塑造语义分类(Number、Range、RegularFunction)而非语法上的限制(HasPlus、Array)。按照 <a rel="nofollow" class="external text" href="https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#t20-avoid-concepts-without-meaningful-semantics">ISO C++ 核心方针 T.20</a> 所说,“与语法限制相反,真正的概念的一个决定性的特征是有能力指定有意义的语义。”</p>
|
|
<h3><span class="mw-headline" id=".E6.A6.82.E5.BF.B5">概念</span></h3>
|
|
<p>概念是要求的具名集合。概念的定义必须在命名空间作用域中出现。</p>
|
|
<p>概念定义拥有以下形式:</p>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td class="t-sdsc-nopad"><code><b>template</b></code> <code><b><</b></code> <span class="t-spar">模板形参列表</span> <code><b>></b></code>
|
|
<p><code><b>concept</b></code> <span class="t-spar">概念名</span> <code><b>=</b></code> <span class="t-spar">约束表达式</span><code><b>;</b></code></p>
|
|
</td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co1">// 概念</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T, <span class="kw1">class</span> U<span class="sy1">></span>
|
|
concept Derived <span class="sy1">=</span> <a href="cpp-types-is_base_of.html"><span class="kw590">std::<span class="me2">is_base_of</span></span></a><span class="sy1"><</span>U, T<span class="sy1">></span><span class="sy4">::</span><span class="me2">value</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>概念不能递归地提及自身,而且不能受约束:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
concept V <span class="sy1">=</span> V<span class="sy1"><</span>T<span class="sy2">*</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 错误:递归的概念</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
concept C1 <span class="sy1">=</span> <span class="kw2">true</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span>C1 T<span class="sy1">></span>
|
|
concept Error1 <span class="sy1">=</span> <span class="kw2">true</span><span class="sy4">;</span> <span class="co1">// 错误:C1 T 试图约束概念定义</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span> requires C1<span class="sy1"><</span>T<span class="sy1">></span>
|
|
concept Error2 <span class="sy1">=</span> <span class="kw2">true</span><span class="sy4">;</span> <span class="co1">// 错误:requires 子句试图约束概念</span></pre></div>
|
|
</div>
|
|
<p>概念不能被显式实例化、显式特化或部分特化(不能更改约束的原初定义的含义)。</p>
|
|
<p>概念可以在标识表达式中命名。该标识表达式的值在满足约束表达式时是 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">true</span></span></span>,否则是 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">false</span></span></span>。</p>
|
|
<p>概念在作为以下内容的一部分时也可以在类型约束中被命名:</p>
|
|
<ul>
|
|
<li><a href="cpp-language-template_parameters.html#.E7.B1.BB.E5.9E.8B.E6.A8.A1.E6.9D.BF.E5.BD.A2.E5.8F.82">类型模板形参声明</a></li>
|
|
<li><a href="cpp-language-auto.html">占位类型说明符</a></li>
|
|
<li><a href="cpp-language-constraints.html#.E5.A4.8D.E5.90.88.E8.A6.81.E6.B1.82">复合要求</a></li>
|
|
</ul>
|
|
<p>概念在 <span class="t-spar">类型约束</span> 中接受的实参要比它的形参列表要求的要少一个,因为按语境推导出的类型会隐式地用作第一个实参:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T, <span class="kw1">class</span> U<span class="sy1">></span>
|
|
concept Derived <span class="sy1">=</span> <a href="cpp-types-is_base_of.html"><span class="kw590">std::<span class="me2">is_base_of</span></span></a><span class="sy1"><</span>U, T<span class="sy1">></span><span class="sy4">::</span><span class="me2">value</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>Derived<span class="sy1"><</span>Base<span class="sy1">></span> T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// T 被 Derived<T, Base> 约束</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E7.BA.A6.E6.9D.9F">约束</span></h3>
|
|
<p>约束是逻辑操作和操作数的序列,它了指定对模板实参的要求。它们可以在 <i>requires 表达式</i>(见下文)中出现,也可以直接作为概念的主体。</p>
|
|
<p>有三种类型的约束:</p>
|
|
<div class="t-li1"><span class="t-li">1)</span> 合取(conjunction)</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 析取(disjunction)</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 不可分割约束(atomic constraint)</div>
|
|
<p>对包含遵循以下顺序的操作数的逻辑与表达式进行<a href="cpp-language-constraints.html#.E7.BA.A6.E6.9D.9F.E8.A7.84.E8.8C.83.E5.8C.96">规范化</a>,确定与一个声明关联的约束:</p>
|
|
<ul>
|
|
<li>每个声明中受约束的<a href="cpp-language-template_parameters.html#.E7.B1.BB.E5.9E.8B.E6.A8.A1.E6.9D.BF.E5.BD.A2.E5.8F.82">类型模板形参</a>或带<a href="cpp-language-auto.html">占位类型</a>声明的非类型模板形参所引入的约束表达式,按出现顺序;</li>
|
|
<li>模板形参列表之后的 <i>requires</i> 子句中的约束表达式;</li>
|
|
<li><a href="cpp-language-function_template.html#.E7.AE.80.E5.86.99.E5.87.BD.E6.95.B0.E6.A8.A1.E6.9D.BF">简写函数模板声明</a>中每个拥有受约束<a href="cpp-language-auto.html">占位类型</a>的形参所引入的约束表达式;</li>
|
|
<li>尾部的 <i>requires</i> 子句中的约束表达式。</li>
|
|
</ul>
|
|
<p>这个顺序决定了在检查是否满足时各个约束时的实例化顺序。</p>
|
|
<p>受约束的声明只能以相同的语法形式重声明。不要求诊断:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span>Incrementable T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span> requires Decrementable<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>Incrementable T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span> requires Decrementable<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span> <span class="co1">// OK:重声明</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
requires Incrementable<span class="sy1"><</span>T<span class="sy1">></span> <span class="sy3">&&</span> Decrementable<span class="sy1"><</span>T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 非良构,不要求诊断</span>
|
|
|
|
<span class="co1">// 下列两个声明拥有不同的约束:</span>
|
|
<span class="co1">// 第一个声明拥有 Incrementable<T> && Decrementable<T></span>
|
|
<span class="co1">// 第二个声明拥有 Decrementable<T> && Incrementable<T></span>
|
|
<span class="co1">// 尽管它们在逻辑上等价</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>Incrementable T<span class="sy1">></span>
|
|
<span class="kw4">void</span> g<span class="br0">(</span>T<span class="br0">)</span> requires Decrementable<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>Decrementable T<span class="sy1">></span>
|
|
<span class="kw4">void</span> g<span class="br0">(</span>T<span class="br0">)</span> requires Incrementable<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 非良构,不要求诊断</span></pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E5.90.88.E5.8F.96">合取</span></h4>
|
|
<p>两个约束的合取是通过在约束表达式中使用 <code>&&</code> 运算符来构成的:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
concept Integral <span class="sy1">=</span> <a href="cpp-types-is_integral.html"><span class="kw456">std::<span class="me2">is_integral</span></span></a><span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">::</span><span class="me2">value</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
concept SignedIntegral <span class="sy1">=</span> Integral<span class="sy1"><</span>T<span class="sy1">></span> <span class="sy3">&&</span> <a href="cpp-types-is_signed.html"><span class="kw516">std::<span class="me2">is_signed</span></span></a><span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">::</span><span class="me2">value</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
concept UnsignedIntegral <span class="sy1">=</span> Integral<span class="sy1"><</span>T<span class="sy1">></span> <span class="sy3">&&</span> <span class="sy3">!</span>SignedIntegral<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>两个约束的合取只有在两个约束都被满足时才会得到满足。合取从左到右短路求值(如果不满足左侧的约束,那么就不会尝试对右侧的约束进行模板实参替换:这样就会防止出现立即语境外的替换所导致的失败)。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
<span class="kw4">constexpr</span> <span class="kw4">bool</span> get_value<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> T<span class="sy4">::</span><span class="me2">value</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
requires <span class="br0">(</span>sizeof<span class="br0">(</span>T<span class="br0">)</span> <span class="sy1">></span> <span class="nu0">1</span> <span class="sy3">&&</span> get_value<span class="sy1"><</span>T<span class="sy1">></span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1</span>
|
|
|
|
<span class="kw4">void</span> f<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
|
|
<span class="kw4">void</span> g<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
f<span class="br0">(</span><span class="st0">'A'</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK,调用 #2。当检查 #1 的约束时,</span>
|
|
<span class="co1">// 不满足 'sizeof(char) > 1',故不检查 get_value<T>()</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E6.9E.90.E5.8F.96">析取</span></h4>
|
|
<p>两个约束的析取,是通过在约束表达式中使用 <code>||</code> 运算符来构成的:</p>
|
|
<p>如果其中一个约束得到满足,那么两个约束的析取的到满足。析取从左到右短路求值(如果满足左侧约束,那么就不会尝试对右侧约束进行模板实参替换)。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T <span class="sy1">=</span> <span class="kw4">void</span><span class="sy1">></span>
|
|
requires EqualityComparable<span class="sy1"><</span>T<span class="sy1">></span> <span class="sy3">||</span> Same<span class="sy1"><</span>T, <span class="kw4">void</span><span class="sy1">></span>
|
|
<span class="kw1">struct</span> equal_to<span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E4.B8.8D.E5.8F.AF.E5.88.86.E5.89.B2.E7.BA.A6.E6.9D.9F">不可分割约束</span></h4>
|
|
<p>不可分割约束由一个表达式 <code>E</code>,和一个从 <code>E</code> 内出现的模板形参到(对受约束实体的各模板形参的有所涉及的)模板实参的映射组成。这种映射被称作<i>形参映射</i>。</p>
|
|
<p>不可分割约束在<a href="cpp-language-constraints.html#.E7.BA.A6.E6.9D.9F.E8.A7.84.E8.8C.83.E5.8C.96">约束规范化</a>过程中形成。<code>E</code> 始终不会是逻辑与(AND)或者逻辑或(OR)表达式(它们分别构成析取和合取)。</p>
|
|
<p>对不可分割约束是否满足的检查会通过替换形参映射和各个模板实参到表达式 <code>E</code> 中来进行。如果替换产生了无效的类型或表达式,那么约束就没有被满足。否则,在任何左值到右值转换后,<code>E</code> 应当是 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span> 类型的纯右值常量表达式,当且仅当它求值为 <code>true</code> 时该约束得以满足。</p>
|
|
<p><code>E</code> 在替换后的类型必须严格为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span>。不能有任何转换:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
<span class="kw1">struct</span> S
|
|
<span class="br0">{</span>
|
|
<span class="kw4">constexpr</span> operator <span class="kw4">bool</span><span class="br0">(</span><span class="br0">)</span> <span class="kw4">const</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="kw2">true</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
requires <span class="br0">(</span>S<span class="sy1"><</span>T<span class="sy1">></span><span class="br0">{</span><span class="br0">}</span><span class="br0">)</span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1</span>
|
|
|
|
<span class="kw4">void</span> f<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
|
|
<span class="kw4">void</span> g<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
f<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:检查 #1 时 S<int>{} 不具有 bool 类型,</span>
|
|
<span class="co1">// 尽管 #2 能更好地匹配</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>如果两个不可分割约束由在源码层面上相同的表达式组成,且它们的形参映射等价,那么认为它们<i>等同</i>。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw4">constexpr</span> <span class="kw4">bool</span> is_meowable <span class="sy1">=</span> <span class="kw2">true</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw4">constexpr</span> <span class="kw4">bool</span> is_cat <span class="sy1">=</span> <span class="kw2">true</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
concept Meowable <span class="sy1">=</span> is_meowable<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
concept BadMeowableCat <span class="sy1">=</span> is_meowable<span class="sy1"><</span>T<span class="sy1">></span> <span class="sy3">&&</span> is_cat<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
concept GoodMeowableCat <span class="sy1">=</span> Meowable<span class="sy1"><</span>T<span class="sy1">></span> <span class="sy3">&&</span> is_cat<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>Meowable T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f1<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>BadMeowableCat T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f1<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>Meowable T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f2<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #3</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>GoodMeowableCat T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f2<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #4</span>
|
|
|
|
<span class="kw4">void</span> g<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
f1<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误,有歧义:</span>
|
|
<span class="co1">// BadMeowableCat 和 Meowable 中的 is_meowable<T></span>
|
|
<span class="co1">// 构成了有区别的不可分割约束且它们并不等同(因此它们不互相包含)</span>
|
|
|
|
f2<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK,调用 #4,它比 #3 更受约束</span>
|
|
<span class="co1">// GoodMeowableCat 从 Meowable 获得其 is_meowable<T></span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E7.BA.A6.E6.9D.9F.E8.A7.84.E8.8C.83.E5.8C.96">约束规范化</span></h4>
|
|
<p><i>约束规范化</i>是将一个约束表达式变换为一个不可分割约束的合取与析取的序列的过程。表达式的<i>范式</i>定义如下:</p>
|
|
<ul>
|
|
<li>表达式 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="br0">(</span>E<span class="br0">)</span></span></span> 的范式就是 <span class="t-c"><span class="mw-geshi cpp source-cpp">E</span></span> 的范式;</li>
|
|
<li>表达式 <span class="t-c"><span class="mw-geshi cpp source-cpp">E1 <span class="sy3">&&</span> E2</span></span> 的范式是 <span class="t-c"><span class="mw-geshi cpp source-cpp">E1</span></span> 和 <span class="t-c"><span class="mw-geshi cpp source-cpp">E2</span></span> 范式的合取;</li>
|
|
<li>表达式 <span class="t-c"><span class="mw-geshi cpp source-cpp">E1 <span class="sy3">||</span> E2</span></span> 的范式是 <span class="t-c"><span class="mw-geshi cpp source-cpp">E1</span></span> 和 <span class="t-c"><span class="mw-geshi cpp source-cpp">E2</span></span> 范式的析取;</li>
|
|
<li>表达式 <span class="t-c"><span class="mw-geshi cpp source-cpp">C<span class="sy1"><</span>A1, A2, ... , AN<span class="sy1">></span></span></span>(其中 <code>C</code> 指名某个概念)的范式,是以 A1, A2, ... , AN 对 C 的每个不可分割约束的形参映射中的 <code>C</code> 的对应模板形参进行替换之后,<code>C</code> 的约束表达式的范式。如果在这种形参映射中的替换产生了无效的类型或表达式,那么程序非良构,不要求诊断。</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
concept A <span class="sy1">=</span> T<span class="sy4">::</span><span class="me2">value</span> <span class="sy3">||</span> <span class="kw2">true</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> U<span class="sy1">></span>
|
|
concept B <span class="sy1">=</span> A<span class="sy1"><</span>U<span class="sy2">*</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// OK:规范化为以下各项的析取</span>
|
|
<span class="co1">// - T::value(映射为 T -> U*)和</span>
|
|
<span class="co1">// - true(映射为空)。</span>
|
|
<span class="co1">// 映射中没有无效类型,尽管 T::value 对所有指针类型均非良构</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> V<span class="sy1">></span>
|
|
concept C <span class="sy1">=</span> B<span class="sy1"><</span>V<span class="sy3">&</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 规范化为以下的析取</span>
|
|
<span class="co1">// - T::value(映射为 T-> V&*)和</span>
|
|
<span class="co1">// - true(映射为空)。</span>
|
|
<span class="co1">// 映射中构成了无效类型 V&* => 非良构,不要求诊断</span></pre></div>
|
|
</div>
|
|
<ul>
|
|
<li>任何其他表达式 <span class="t-c"><span class="mw-geshi cpp source-cpp">E</span></span> 的范式是一条不可分割约束,它的表达式是 <span class="t-c"><span class="mw-geshi cpp source-cpp">E</span></span> 而它的形参映射是恒等映射。这包括所有<a href="cpp-language-fold.html">折叠表达式</a>,甚至包括以 <code>&&</code> 或 <code>||</code> 运算符进行的折叠。</li>
|
|
</ul>
|
|
<p>用户定义的 <code>&&</code> 或 <code>||</code> 重载在约束规范化上无效。</p>
|
|
<h3><span class="mw-headline" id="requires_.E5.AD.90.E5.8F.A5">requires 子句</span></h3>
|
|
<p>关键词 <span class="t-c"><span class="mw-geshi cpp source-cpp">requires</span></span> 用来引入 <i>requires 子句</i>,它指定对各模板实参,或对函数声明的约束。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T<span class="sy3">&&</span><span class="br0">)</span> requires Eq<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 可以作为函数声明符的末尾元素出现</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span> requires Addable<span class="sy1"><</span>T<span class="sy1">></span> <span class="co1">// 或者在模板形参列表的右边</span>
|
|
T add<span class="br0">(</span>T a, T b<span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> a <span class="sy2">+</span> b<span class="sy4">;</span> <span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>这种情况下,关键词 <i>requires</i> 必须后随某个常量表达式(因此可以写成 <span class="t-c"><span class="mw-geshi cpp source-cpp">requires <span class="kw2">true</span></span></span>),但这是为了使用一个具名概念(如上例),具名概念的一条合取/析取,或者一个 <i>requires 表达式</i>。</p>
|
|
<p>表达式必须具有下列形式之一:</p>
|
|
<ul>
|
|
<li><a href="cpp-language-expressions.html#.E5.88.9D.E7.AD.89.E8.A1.A8.E8.BE.BE.E5.BC.8F">初等表达式</a>,例如 <span class="t-c"><span class="mw-geshi cpp source-cpp">Swappable<span class="sy1"><</span>T<span class="sy1">></span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-types-is_integral.html"><span class="kw456">std::<span class="me2">is_integral</span></span></a><span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">::</span><span class="me2">value</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="br0">(</span><a href="cpp-types-is_object.html"><span class="kw487">std::<span class="me2">is_object_v</span></span></a><span class="sy1"><</span>Args<span class="sy1">></span> <span class="sy3">&&</span> ...<span class="br0">)</span></span></span> 或任何带括号的表达式</li>
|
|
<li>以运算符 <code>&&</code> 联结的初等表达式的序列</li>
|
|
<li>以运算符 <code>||</code> 联结的前述表达式的序列</li>
|
|
</ul>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw4">constexpr</span> <span class="kw4">bool</span> is_meowable <span class="sy1">=</span> <span class="kw2">true</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw4">constexpr</span> <span class="kw4">bool</span> is_purrable<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="kw2">true</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span> requires is_meowable<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span> <span class="co1">// OK</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw4">void</span> g<span class="br0">(</span>T<span class="br0">)</span> requires is_purrable<span class="sy1"><</span>T<span class="sy1">></span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 错误:is_purrable<T>() 不是初等表达式</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw4">void</span> h<span class="br0">(</span>T<span class="br0">)</span> requires <span class="br0">(</span>is_purrable<span class="sy1"><</span>T<span class="sy1">></span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id="requires_.E8.A1.A8.E8.BE.BE.E5.BC.8F">requires 表达式</span></h3>
|
|
<p>关键词 <span class="t-c"><span class="mw-geshi cpp source-cpp">requires</span></span> 也用来开始一个 <i>requires 表达式</i>,它是 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">bool</span></span></span> 类型的纯右值表达式,描述对一些模板实参的约束。这种表达式在约束得到满足时是 <code>true</code>,否则是 <code>false</code>:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
concept Addable <span class="sy1">=</span> requires <span class="br0">(</span>T x<span class="br0">)</span> <span class="br0">{</span> x <span class="sy2">+</span> x<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span> <span class="co1">// requires 表达式</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span> requires Addable<span class="sy1"><</span>T<span class="sy1">></span> <span class="co1">// requires 子句,不是 requires 表达式</span>
|
|
T add<span class="br0">(</span>T a, T b<span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> a <span class="sy2">+</span> b<span class="sy4">;</span> <span class="br0">}</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
requires requires <span class="br0">(</span>T x<span class="br0">)</span> <span class="br0">{</span> x <span class="sy2">+</span> x<span class="sy4">;</span> <span class="br0">}</span> <span class="co1">// 随即的约束,注意关键字被使用两次</span>
|
|
T add<span class="br0">(</span>T a, T b<span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> a <span class="sy2">+</span> b<span class="sy4">;</span> <span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p><i>requires 表达式</i>的语法如下:</p>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td class="t-sdsc-nopad"><code><b>requires</b></code> <code><b>{</b></code> <span class="t-spar">要求序列</span> <code><b>}</b></code></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td class="t-sdsc-nopad"><code><b>requires</b></code> <code><b>(</b></code> <span class="t-spar">形参列表</span><span class="t-mark">(可选)</span> <code><b>)</b></code> <code><b>{</b></code> <span class="t-spar">要求序列</span> <code><b>}</b></code></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-par-begin">
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">形参列表</span></td>
|
|
<td>-</td>
|
|
<td>与函数声明中类似的以逗号分隔的形参列表,但不能有默认实参且不能以(并非指定包展开的)省略号结尾。这些形参没有存储期、连接或生存期,它们只会用来辅助进行各个要求的制定。这些形参在 <span class="t-spar">要求序列</span> 的闭 <code><b>}</b></code> 前处于作用域中。</td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">要求序列</span></td>
|
|
<td>-</td>
|
|
<td><i>要求(requirement)</i>的序列,描述于下(每个要求以分号结尾)。</td>
|
|
</tr>
|
|
</table>
|
|
<p><span class="t-spar">要求序列</span> 中的每个要求是下列之一:</p>
|
|
<ul>
|
|
<li>简单要求(simple requirement)</li>
|
|
<li>类型要求(type requirement)</li>
|
|
<li>复合要求(compound requirement)</li>
|
|
<li>嵌套要求(nested requirement)</li>
|
|
</ul>
|
|
<p>要求可以提及处于作用域中的模板形参,由 <span class="t-spar">形参列表</span> 引入的局部形参,以及从它的外围语境中可见的任何其他声明。</p>
|
|
<p>对<a href="cpp-language-templates.html#.E6.A8.A1.E6.9D.BF.E5.8C.96.E5.AE.9E.E4.BD.93">模板化实体</a>的声明中所使用的 requires 表达式进行模板实参替换,可能导致在其要求中形成无效的类型或表达式,或违反这些要求的语义约束。这些情况下,该 requires 表达式求值为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">false</span></span></span> 而不会导致程序非良构。替换和语义约束检查按词法顺序执行,并在遇到确定 requires 表达式结果的条件时停止。如果替换(如果存在)和语义约束检查成功,那么 requires 表达式求值为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">true</span></span></span>。</p>
|
|
<p>如果对于每一种可能的模板实参 requires 表达式中都会出现替换失败,那么程序非良构,不要求诊断:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
concept C <span class="sy1">=</span> requires
|
|
<span class="br0">{</span>
|
|
new <span class="kw4">int</span><span class="br0">[</span><span class="sy2">-</span><span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span>sizeof<span class="br0">(</span>T<span class="br0">)</span><span class="br0">]</span><span class="sy4">;</span> <span class="co1">// 对每个 T 均为无效:非良构,不要求诊断</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>如果 requires 表达式在它的约束中含有无效的类型或表达式,而它并不在<a href="cpp-language-templates.html#.E6.A8.A1.E6.9D.BF.E5.8C.96.E5.AE.9E.E4.BD.93">模板化实体</a>的声明内出现,那么程序非良构。</p>
|
|
<h4><span class="mw-headline" id=".E7.AE.80.E5.8D.95.E8.A6.81.E6.B1.82">简单要求</span></h4>
|
|
<p>简单要求是不以 <code>requires</code> 关键词起始的任意表达式语句。它断言该表达式合法。该表达式是不求值操作数;只检查语言正确性。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
concept Addable <span class="sy1">=</span> requires <span class="br0">(</span>T a, T b<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
a <span class="sy2">+</span> b<span class="sy4">;</span> <span class="co1">// “表达式 a + b 是可编译的合法表达式”</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">class</span> T, <span class="kw1">class</span> U <span class="sy1">=</span> T<span class="sy1">></span>
|
|
concept Swappable <span class="sy1">=</span> requires<span class="br0">(</span>T<span class="sy3">&&</span> t, U<span class="sy3">&&</span> u<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
swap<span class="br0">(</span><a href="cpp-utility-forward.html"><span class="kw1117">std::<span class="me2">forward</span></span></a><span class="sy1"><</span>T<span class="sy1">></span><span class="br0">(</span>t<span class="br0">)</span>, <a href="cpp-utility-forward.html"><span class="kw1117">std::<span class="me2">forward</span></span></a><span class="sy1"><</span>U<span class="sy1">></span><span class="br0">(</span>u<span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
swap<span class="br0">(</span><a href="cpp-utility-forward.html"><span class="kw1117">std::<span class="me2">forward</span></span></a><span class="sy1"><</span>U<span class="sy1">></span><span class="br0">(</span>u<span class="br0">)</span>, <a href="cpp-utility-forward.html"><span class="kw1117">std::<span class="me2">forward</span></span></a><span class="sy1"><</span>T<span class="sy1">></span><span class="br0">(</span>t<span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<p>以 <code>requires</code> 关键词起始的要求始终会被解读成嵌套要求。因此简单要求不能以无括号的 requires 表达式起始。</p>
|
|
<h4><span class="mw-headline" id=".E7.B1.BB.E5.9E.8B.E8.A6.81.E6.B1.82">类型要求</span></h4>
|
|
<p>类型要求是关键词 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">typename</span></span></span> 后随一个可以有限定的类型名。它的要求是该类型名合法:这可以用来校验某个具名嵌套类型存在,或某个类模板特化指名一个类型,或某个别名模板特化指名一个类型。指名类模板特化的类型要求不要求该类型完整。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
<span class="kw1">using</span> Ref <span class="sy1">=</span> T<span class="sy3">&</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
concept C <span class="sy1">=</span> requires
|
|
<span class="br0">{</span>
|
|
<span class="kw1">typename</span> T<span class="sy4">::</span><span class="me2">inner</span><span class="sy4">;</span> <span class="co1">// 要求的嵌套成员名</span>
|
|
<span class="kw1">typename</span> S<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 要求的类模板特化</span>
|
|
<span class="kw1">typename</span> Ref<span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 要求的别名模板替换</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">class</span> T, <span class="kw1">class</span> U<span class="sy1">></span>
|
|
<span class="kw1">using</span> CommonType <span class="sy1">=</span> <a href="cpp-types-common_type.html"><span class="kw636">std::<span class="me2">common_type_t</span></span></a><span class="sy1"><</span>T, U<span class="sy1">></span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">class</span> T, <span class="kw1">class</span> U<span class="sy1">></span>
|
|
concept Common <span class="sy1">=</span> requires <span class="br0">(</span>T t, U u<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="kw1">typename</span> CommonType<span class="sy1"><</span>T, U<span class="sy1">></span><span class="sy4">;</span> <span class="co1">// CommonType<T, U> 合法并指名一个类型</span>
|
|
<span class="br0">{</span> CommonType<span class="sy1"><</span>T, U<span class="sy1">></span><span class="br0">{</span><a href="cpp-utility-forward.html"><span class="kw1117">std::<span class="me2">forward</span></span></a><span class="sy1"><</span>T<span class="sy1">></span><span class="br0">(</span>t<span class="br0">)</span><span class="br0">}</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="br0">{</span> CommonType<span class="sy1"><</span>T, U<span class="sy1">></span><span class="br0">{</span><a href="cpp-utility-forward.html"><span class="kw1117">std::<span class="me2">forward</span></span></a><span class="sy1"><</span>U<span class="sy1">></span><span class="br0">(</span>u<span class="br0">)</span><span class="br0">}</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E5.A4.8D.E5.90.88.E8.A6.81.E6.B1.82">复合要求</span></h4>
|
|
<p>复合要求的形式为</p>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td class="t-sdsc-nopad"><code><b>{</b></code> <span class="t-spar">表达式</span> <code><b>}</b></code> <code><b>noexcept</b></code><span class="t-mark">(可选)</span> <span class="t-spar">返回类型要求</span><span class="t-mark">(可选)</span> <code><b>;</b></code></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-par-begin">
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">返回类型要求</span></td>
|
|
<td>-</td>
|
|
<td><code><b>-></b></code> <span class="t-spar">类型约束</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>并断言该具名表达式的各项性质。以下列顺序进行替换和语义约束检查:</p>
|
|
<div class="t-li1"><span class="t-li">1)</span> 替换模板实参(若存在)到 <span class="t-spar">表达式</span> 中;</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 如果使用了 <code>noexcept</code>,那么 <span class="t-spar">表达式</span> 必须<a href="cpp-language-noexcept.html">不会抛出</a>;</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 如果出现了 <span class="t-spar">返回类型规定</span>,那么:</div>
|
|
<div class="t-li2"><span class="t-li">a)</span> 替换模板实参到 <span class="t-spar">返回类型规定</span> 中;</div>
|
|
<div class="t-li2"><span class="t-li">b)</span> <span class="t-c"><span class="mw-geshi cpp source-cpp">decltype<span class="br0">(</span><span class="br0">(</span>表达式<span class="br0">)</span><span class="br0">)</span></span></span> 必须满足有该 <span class="t-spar">类型约束</span> 所蕴含的约束。否则,外围 requires 表达式为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">false</span></span></span>。</div>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
concept C2 <span class="sy1">=</span> requires<span class="br0">(</span>T x<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="co1">// 表达式 *x 必须合法</span>
|
|
<span class="co1">// 并且类型 T::inner 必须合法</span>
|
|
<span class="co1">// 并且 *x 的结果必须可以转换为 T::inner</span>
|
|
<span class="br0">{</span><span class="sy2">*</span>x<span class="br0">}</span> <span class="sy2">-</span><span class="sy1">></span> std<span class="sy4">::</span><span class="me2">convertible_to</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy4">::</span><span class="me2">inner</span><span class="sy1">></span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 表达式 x + 1 必须合法</span>
|
|
<span class="co1">// 并且 std::Same<decltype((x + 1)), int> 必须被满足</span>
|
|
<span class="co1">// 也就是说,(x + 1) 必须是 int 类型的纯右值</span>
|
|
<span class="br0">{</span>x <span class="sy2">+</span> <span class="nu0">1</span><span class="br0">}</span> <span class="sy2">-</span><span class="sy1">></span> std<span class="sy4">::</span><span class="me2">same_as</span><span class="sy1"><</span><span class="kw4">int</span><span class="sy1">></span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 表达式 x * 1 必须合法</span>
|
|
<span class="co1">// 并且它的结果必须可以转换到 T</span>
|
|
<span class="br0">{</span>x <span class="sy2">*</span> <span class="nu0">1</span><span class="br0">}</span> <span class="sy2">-</span><span class="sy1">></span> std<span class="sy4">::</span><span class="me2">convertible_to</span><span class="sy1"><</span>T<span class="sy1">></span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<h4><span class="mw-headline" id=".E5.B5.8C.E5.A5.97.E8.A6.81.E6.B1.82">嵌套要求</span></h4>
|
|
<p>嵌套要求的形式为</p>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td class="t-sdsc-nopad"><code><b>requires</b></code> <span class="t-spar">约束表达式</span> <code><b>;</b></code></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<p>它可以用来以局部形参来指定额外的约束。<span class="t-spar">约束表达式</span> 必须被所替换的模板实参(如果存在)所满足。在嵌套要求中进行模板实参的替换所导致的在 <span class="t-spar">约束表达式</span> 中的替换只会进行到足以确定 <span class="t-spar">约束表达式</span> 是否得到满足所需的程度。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span> <span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
concept Semiregular <span class="sy1">=</span> DefaultConstructible<span class="sy1"><</span>T<span class="sy1">></span> <span class="sy3">&&</span>
|
|
CopyConstructible<span class="sy1"><</span>T<span class="sy1">></span> <span class="sy3">&&</span> Destructible<span class="sy1"><</span>T<span class="sy1">></span> <span class="sy3">&&</span> CopyAssignable<span class="sy1"><</span>T<span class="sy1">></span> <span class="sy3">&&</span>
|
|
requires<span class="br0">(</span>T a, size_t n<span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
requires Same<span class="sy1"><</span>T<span class="sy2">*</span>, decltype<span class="br0">(</span><span class="sy3">&</span>a<span class="br0">)</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 嵌套:“Same<...> 求值为 true”</span>
|
|
<span class="br0">{</span> a.~T<span class="br0">(</span><span class="br0">)</span> <span class="br0">}</span> <span class="kw1">noexcept</span><span class="sy4">;</span> <span class="co1">// 复合:"a.~T()" 是不抛出的合法表达式</span>
|
|
requires Same<span class="sy1"><</span>T<span class="sy2">*</span>, decltype<span class="br0">(</span>new T<span class="br0">)</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 嵌套:“Same<...> 求值为 true”</span>
|
|
requires Same<span class="sy1"><</span>T<span class="sy2">*</span>, decltype<span class="br0">(</span>new T<span class="br0">[</span>n<span class="br0">]</span><span class="br0">)</span><span class="sy1">></span><span class="sy4">;</span> <span class="co1">// 嵌套</span>
|
|
<span class="br0">{</span> delete new T <span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 复合</span>
|
|
<span class="br0">{</span> delete new T<span class="br0">[</span>n<span class="br0">]</span> <span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 复合</span>
|
|
<span class="br0">}</span><span class="sy4">;</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E7.BA.A6.E6.9D.9F.E7.9A.84.E5.81.8F.E5.BA.8F">约束的偏序</span></h3>
|
|
<p>在任何进一步的分析之前都会对各个约束进行<a href="cpp-language-constraints.html#.E7.BA.A6.E6.9D.9F.E8.A7.84.E8.8C.83.E5.8C.96">规范化</a>,对每个具名概念的主体和每个 requires 表达式进行替换,直到剩下不可分割约束的合取与析取的序列为止。</p>
|
|
<p>如果根据约束 <code>P</code> 和约束 <code>Q</code> 中的各不可分割约束的同一性可以证明 <code>P</code> <a href="https://en.wikipedia.org/wiki/Logical_consequence" class="extiw" title="enwiki:Logical consequence">蕴含</a> <code>Q</code>,那么称 <code>P</code> <i>归入(subsume)</i> <code>Q</code>。(并进行类型和表达式的等价性分析:<code>N > 0</code> 并不归入 <code>N >= 0</code>)。</p>
|
|
<p>具体来说,首先转换 <code>P</code> 为析取范式并转换 <code>Q</code> 为合取范式。当且仅当以下情况下 <code>P</code> 归入 <code>Q</code>:</p>
|
|
<ul>
|
|
<li><code>P</code> 的析取范式中的每个析取子句都能归入 <code>Q</code> 的合取范式中的每个合取子句,其中</li>
|
|
<li>当且仅当析取子句中存在不可分割约束 <code>U</code> 而合取子句中存在不可分割约束 <code>V</code>,使得 <code>U</code> 归入 <code>V</code> 时,析取子句能归入合取子句;</li>
|
|
<li>当且仅当使用<a href="cpp-language-constraints.html#.E4.B8.8D.E5.8F.AF.E5.88.86.E5.89.B2.E7.BA.A6.E6.9D.9F">上文</a>所述的规则判定为等同时,称不可分割约束 <code>A</code> 能归入不可分割约束 <code>B</code>。</li>
|
|
</ul>
|
|
<p>归入关系定义了约束的偏序,用于确定:</p>
|
|
<ul>
|
|
<li><a href="cpp-language-overload_resolution.html">重载决议</a>中非模板函数的最佳可行候选</li>
|
|
<li>重载集中的<a href="cpp-language-overloaded_address.html">非模板函数的地址</a></li>
|
|
<li>模板模板实参的最佳匹配</li>
|
|
<li>类模板特化的偏序</li>
|
|
<li>函数模板的<a href="cpp-language-function_template.html#.E5.87.BD.E6.95.B0.E6.A8.A1.E6.9D.BF.E9.87.8D.E8.BD.BD">偏序</a></li>
|
|
</ul>
|
|
<table class="metadata plainlinks ambox mbox-small-left ambox-notice" style="">
|
|
<tr>
|
|
<td class="mbox-empty-cell"></td>
|
|
<td class="mbox-text" style="">本节未完成<br />
|
|
原因:从上述内容添加到此的回链</td>
|
|
</tr>
|
|
</table>
|
|
<p>如果声明 <code>D1</code> 和 <code>D2</code> 均受约束,且 <code>D1</code> 关联的约束能归入 <code>D2</code> 关联的约束,(或 <code>D2</code> 没有约束),那么称 <code>D1</code> 与 <code>D2</code> 相比<i>至少一样受约束</i>。如果 <code>D1</code> 至少与 <code>D2</code> 一样受约束,而 <code>D2</code> 并非至少与 <code>D1</code> 一样受约束,那么 <code>D1</code> 比 <code>D2</code> <i>更受约束</i>。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
concept Decrementable <span class="sy1">=</span> requires<span class="br0">(</span>T t<span class="br0">)</span> <span class="br0">{</span> <span class="sy2">--</span>t<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
concept RevIterator <span class="sy1">=</span> Decrementable<span class="sy1"><</span>T<span class="sy1">></span> <span class="sy3">&&</span> requires<span class="br0">(</span>T t<span class="br0">)</span> <span class="br0">{</span> <span class="sy2">*</span>t<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// RevIterator 能归入 Decrementable,但反之不行</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>Decrementable T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #1</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>RevIterator T<span class="sy1">></span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #2,比 #1 更受约束</span>
|
|
|
|
f<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// int 只满足 Decrementable,选择 #1</span>
|
|
f<span class="br0">(</span><span class="br0">(</span><span class="kw4">int</span><span class="sy2">*</span><span class="br0">)</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// int* 满足两个约束,选择 #2,因为它更受约束</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">class</span> T<span class="sy1">></span>
|
|
<span class="kw4">void</span> g<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #3(无约束)</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>Decrementable T<span class="sy1">></span>
|
|
<span class="kw4">void</span> g<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #4</span>
|
|
|
|
g<span class="br0">(</span><span class="kw2">true</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// bool 不满足 Decrementable,选择 #3</span>
|
|
g<span class="br0">(</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// int 满足 Decrementable,选择 #4,因为它更受约束</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> T<span class="sy1">></span>
|
|
concept RevIterator2 <span class="sy1">=</span> requires<span class="br0">(</span>T t<span class="br0">)</span> <span class="br0">{</span> <span class="sy2">--</span>t<span class="sy4">;</span> <span class="sy2">*</span>t<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>Decrementable T<span class="sy1">></span>
|
|
<span class="kw4">void</span> h<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #5</span>
|
|
|
|
<span class="kw1">template</span><span class="sy1"><</span>RevIterator2 T<span class="sy1">></span>
|
|
<span class="kw4">void</span> h<span class="br0">(</span>T<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// #6</span>
|
|
|
|
h<span class="br0">(</span><span class="br0">(</span><span class="kw4">int</span><span class="sy2">*</span><span class="br0">)</span><span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 有歧义</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E5.85.B3.E9.94.AE.E8.AF.8D">关键词</span></h3>
|
|
<p><a href="cpp-keyword-concept.html"><tt>concept</tt></a>, <a href="cpp-keyword-requires.html"><tt>requires</tt></a></p>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|