188188
189189 < p class ="text-right "> < small >
190190 最終更新日時(UTC):
191- < span itemprop ="datePublished " content ="2026-04-07T13:06:31 ">
192- 2026年04月07日 13時06分31秒
191+ < span itemprop ="datePublished " content ="2026-04-08T09:25:22 ">
192+ 2026年04月08日 09時25分22秒
193193 </ span >
194194 < br />
195195 < span itemprop ="author " itemscope itemtype ="http://schema.org/Person ">
@@ -219,7 +219,7 @@ <h1 itemprop="name"><span class="token">制約式内での畳み込み式の順
219219< p > </ p >
220220< h2 > 概要</ h2 >
221221< p > C++26では、制約式 (constraint) 内で使用される畳み込み式 (fold expression) に対して、包摂関係 (subsumption) の規則を導入する。</ p >
222- < p > C++23では、< code > (C<T> && ...)</ code > のような畳み込み式は構文上は連言 (conjunction) や選言 (disjunction) のように見えるが、制約の順序付けにおいては単一の原子制約 (atomic constraint) として扱われていた。そのため、概念的には一方がもう一方をより強く制約しているにもかかわらず、< a class ="cpprefjp-defined-word " data-desc ="関数呼び出し時に、同名の関数の中から実際に呼び出す関数を決定する処理。このときの候補になることを、オーバーロード解決に参加するという "> オーバーロード解決</ a > で曖昧になるケースがあった。</ p >
222+ < p > C++23では、< code > (C<T> && ...)</ code > のような畳み込み式は制約の順序付けにおいて単一の原子制約 (atomic constraint) として扱われていた。そのため、概念的には一方がもう一方をより強く制約しているにもかかわらず、< a class ="cpprefjp-defined-word " data-desc ="関数呼び出し時に、同名の関数の中から実際に呼び出す関数を決定する処理。このときの候補になることを、オーバーロード解決に参加するという "> オーバーロード解決</ a > で曖昧になるケースがあった。</ p >
223223< p > < div class ="codehilite "> < pre > < span > </ span > < code > < span class ="k "> template</ span > < span class ="w "> </ span > < span class ="o "> <</ span > < span class ="k "> class</ span > < span class ="w "> </ span > < span class ="nc "> T</ span > < span class ="o "> ></ span >
224224< span class ="k "> concept</ span > < span class ="w "> </ span > < span class ="nc "> A</ span > < span class ="w "> </ span > < span class ="o "> =</ span > < span class ="w "> </ span > < span class ="n "> < a href ="../../reference/type_traits/is_move_constructible.html "> std::is_move_constructible_v</ a > </ span > < span class ="o "> <</ span > < span class ="n "> T</ span > < span class ="o "> ></ span > < span class ="p "> ;</ span >
225225
@@ -242,17 +242,20 @@ <h2>概要</h2>
242242< span class ="n "> g</ span > < span class ="p "> (</ span > < span class ="n "> < a href ="../../reference/vector/vector.html "> std::vector</ a > </ span > < span class ="p "> {</ span > < span class ="mi "> 1</ span > < span class ="p "> ,</ span > < span class ="w "> </ span > < span class ="mi "> 2</ span > < span class ="p "> ,</ span > < span class ="w "> </ span > < span class ="mi "> 3</ span > < span class ="p "> });</ span >
243243</ code > </ pre > </ div >
244244</ p >
245- < p > C++26では、< code > && </ code > による畳み込み式は個別の制約の連言として、 < code > || </ code > による畳み込み式は個別の制約の選言として正規化され、通常の原子制約と同じ包摂規則が適用される 。</ p >
245+ < p > C++26では、制約の種類として新たに「折りたたみ展開制約 (fold expanded constraint)」が導入され、畳み込み式を含む制約の間で包摂関係が認識されるようになる 。</ p >
246246< h2 > 仕様</ h2 >
247- < h3 > 畳み込み式の正規化</ h3 >
248- < p > 畳み込み式が制約として使用される場合、以下のように正規化される:</ p >
247+ < h3 > 折りたたみ展開制約 (fold expanded constraint)</ h3 >
248+ < p > C++26では、制約の種類として連言 (conjunction)、選言 (disjunction)、原子制約 (atomic constraint) に加え、「折りたたみ展開制約 (fold expanded constraint)」(P2963R3) および「コンセプト依存制約 (concept-dependent constraint)」(P2841R7) が新たに導入される。ここでは折りたたみ展開制約について説明する。</ p >
249+ < p > 畳み込み式 < code > (E && ...)</ code > や < code > (E || ...)</ code > が制約の正規化において処理される際、通常は折りたたみ展開制約として正規化される。折りたたみ展開制約は、制約< code > E</ code > の正規形と畳み込み演算子 (< code > &&</ code > または< code > ||</ code > ) の組から構成される。</ p >
250+ < p > ただし、< code > E</ code > が展開されていないコンセプトテンプレートパラメータパック (unexpanded concept template parameter pack) を含む場合は、そのパックの要素数< code > N</ code > に基づいて< code > E_0 Op ... Op E_{N-1}</ code > の形に展開され、連言または選言にまで分解される。これはP2841R7 (コンセプトテンプレートパラメータ) との連携による動作である。</ p >
251+ < h3 > 包摂の規則</ h3 >
252+ < p > 折りたたみ展開制約< code > A</ code > がもうひとつの折りたたみ展開制約< code > B</ code > を包摂するのは、以下の条件をすべて満たす場合である:</ p >
249253< ul >
250- < li > < code > (C<T> && ...) </ code > は < code > C<T1> && C<T2> && ... && C<Tn> </ code > のような連言として扱われる </ li >
251- < li > < code > (C<T> || ...) </ code > は < code > C<T1> || C<T2> || ... || C<Tn> </ code > のような選言として扱われる </ li >
252- < li > < code > (... && C<T>) </ code > や二項畳み込み式 < code > (init && ... && C<T>) </ code > も同様に正規化される </ li >
254+ < li > < code > A </ code > と < code > B </ code > が包摂の互換性 (compatible for subsumption) をもつこと。すなわち、それぞれの制約が同等の展開されていないパックを含むこと </ li >
255+ < li > < code > A </ code > と < code > B </ code > が同じ畳み込み演算子 ( < code > && </ code > または < code > || </ code > ) をもつこと </ li >
256+ < li > < code > A </ code > の制約が < code > B </ code > の制約を包摂すること </ li >
253257</ ul >
254- < h3 > 包摂の規則</ h3 >
255- < p > 畳み込み式の制約間の包摂は、同じ畳み込み演算子 (< code > &&</ code > または< code > ||</ code > ) を使用している場合にのみ成立する。異なる畳み込み演算子間の包摂は行われない。</ p >
258+ < p > 異なる畳み込み演算子間の包摂は行われない。</ p >
256259< p > < div class ="codehilite "> < pre > < span > </ span > < code > < span class ="k "> template</ span > < span class ="w "> </ span > < span class ="o "> <</ span > < span class ="k "> class</ span > < span class ="w "> </ span > < span class ="nc "> T</ span > < span class ="o "> ></ span >
257260< span class ="k "> concept</ span > < span class ="w "> </ span > < span class ="nc "> C</ span > < span class ="w "> </ span > < span class ="o "> =</ span > < span class ="w "> </ span > < span class ="cm "> /* ... */</ span > < span class ="p "> ;</ span >
258261
@@ -263,16 +266,15 @@ <h3>包摂の規則</h3>
263266< span class ="kt "> void</ span > < span class ="w "> </ span > < span class ="n "> f</ span > < span class ="p "> ()</ span > < span class ="w "> </ span > < span class ="k "> requires</ span > < span class ="w "> </ span > < span class ="p "> (</ span > < span class ="n "> C</ span > < span class ="o "> <</ span > < span class ="n "> T</ span > < span class ="o "> ></ span > < span class ="w "> </ span > < span class ="o "> &&</ span > < span class ="w "> </ span > < span class ="p "> ...);</ span > < span class ="w "> </ span > < span class ="c1 "> // #2</ span >
264267
265268< span class ="c1 "> // &&と||は異なる演算子なので、#2が#1を包摂するとはみなされない</ span >
266- < span class ="c1 "> // f()の呼び出しは曖昧</ span >
267- < span class ="n "> f</ span > < span class ="p "> ();</ span >
269+ < span class ="n "> f</ span > < span class ="p "> ();</ span > < span class ="w "> </ span > < span class ="c1 "> // 曖昧</ span >
268270</ code > </ pre > </ div >
269271</ p >
270272< h3 > 短絡評価</ h3 >
271- < p > 畳み込み式の制約の評価と充足 (satisfaction) は、通常の連言/選言と同様に短絡評価される。</ p >
273+ < p > 折りたたみ展開制約の充足 (satisfaction) は、通常の連言/選言と同様に短絡評価される。 < code > && </ code > の場合は充足されない最初の要素で、 < code > || </ code > の場合は充足される最初の要素で評価が停止する 。</ p >
272274< h2 > この機能が必要になった背景・経緯</ h2 >
273275< p > コンセプトと畳み込み式は、可変引数テンプレートの制約を簡潔に記述するために組み合わせて使われることが多い。しかし、C++23では畳み込み式が原子制約として扱われるため、概念的に明らかな包摂関係が認識されず、< a class ="cpprefjp-defined-word " data-desc ="関数呼び出し時に、同名の関数の中から実際に呼び出す関数を決定する処理。このときの候補になることを、オーバーロード解決に参加するという "> オーバーロード解決</ a > が曖昧になる問題があった。</ p >
274276< p > たとえば、< code > < a href ="../../reference/ranges/bidirectional_range.html "> std::ranges::bidirectional_range</ a > </ code > と< code > < a href ="../../reference/ranges/random_access_range.html "> std::ranges::random_access_range</ a > </ code > は非可変引数テンプレートでは正しく順序付けられるが、可変引数テンプレートで畳み込み式を使用すると順序付けが失われていた。この提案はその問題を解決する。</ p >
275- < p > この提案はP2841R0 (コンセプトと変数テンプレートのテンプレートテンプレートパラメータ) から派生したもので、畳み込み式のパターンが通常の制約式である場合を扱う。パターンがコンセプトテンプレートパラメータである場合はP2841で扱われる 。</ p >
277+ < p > この提案はP2841R0から派生したもので、折りたたみ展開制約の導入と包摂規則を定める。畳み込み式のパターンにコンセプトテンプレートパラメータパック (P2841で導入) が含まれる場合は、連言/選言にまで完全に分解される 。</ p >
276278< h2 > < a href ="#relative-page " id ="relative-page "> 関連項目</ a > </ h2 >
277279< ul >
278280< li > < a href ="../cpp17/folding_expressions.html "> C++17 畳み込み式</ a > </ li >
0 commit comments