Skip to content

Commit

Permalink
if_constexpr: 推敲
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed Jul 6, 2019
1 parent 46f7bb9 commit 08a8015
Showing 1 changed file with 24 additions and 23 deletions.
47 changes: 24 additions & 23 deletions lang/cpp17/if_constexpr.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ else
```
`condition`はコンパイル時に`bool`に評価できる式である。
`condition`によって採用されなかった分岐は、2段階名前探索において
`condition`によって採用されなかった分岐は、2段階名前探索(two-phase name lookup)において
2段階目の依存式の解析(依存名解決およびテンプレートの実体化)の対象から除外される。
ただし、どちらの分岐も1段階目の構文解析・意味解析の対象となる事に注意する。
Expand Down Expand Up @@ -69,7 +69,7 @@ constexpr if文の導入によりそのような複雑な手法を用いずに

### 2段階名前探索における注意点

`constexpr if`文で、実行されない方の`statement`は廃棄文(discarded statement)となり、文の実体化を防ぐ。言い換えると、Two Phase Name Look-upにおける`dependent name`(以下、依存名)は、廃棄文の場合検証されない。また文が実体化されないのだから通常のif文と同じくもちろん実行時に実行もされない。つまり次の例は意図と異なる挙動を示す。
`constexpr if`文で、実行されない方の`statement`は廃棄文(discarded statement)となり、文の実体化を防ぐ。言い換えると、2段階名前探索における依存名(dependent name)は、廃棄文の場合検証されない。また文が実体化されないのだから通常のif文と同じくもちろん実行時に実行もされない。つまり次の例は意図と異なる挙動を示す。

```cpp example
#include <type_traits>
Expand Down Expand Up @@ -165,7 +165,7 @@ int main()
### 類似機能との比較
`constexpr if`文の導入によってC++の`if`系の条件分岐文は3種類になった
`constexpr if`文の導入によってC++の`if`系の条件分岐は3種類になった
- プリプロセス時`if`: `#if`
- コンパイル時`if`: `constexpr if`
Expand Down Expand Up @@ -245,10 +245,10 @@ int main()
## この機能が必要になった背景・経緯

一番最初の静的な条件分岐の提案 N3322 の直接のきっかけになったのは、
C++11における静的な条件によってコンパイルエラーを発生させる`static_assert`の導入である
静的な条件によってコンパイルエラーを発生させる `static_assert` の C++11 への導入である
その拡張として静的な条件によって宣言を切り替えられる機能を考えるのは自然な発想である。
N3322では`static_assert`と同じように、
名前空間スコープ・クラススコープ・ブロックスコープの何れでも使える`static_if`を提案している。
N3322 では`static_assert` と同じように、
名前空間スコープ・クラススコープ・ブロックスコープの何れでも使える `static_if` を提案している。
次の提案 N3329 ではD言語における実装 [D0.124 `static if` (2005年)、D2.015 Template Constraints (2008年)] の実績を元に、
より詳しい提案を行っている。

Expand All @@ -268,30 +268,30 @@ N3322では、`static_assert`と同じように、
- 静的な条件分岐の構文は新しいスコープを作らない。つまり条件分岐内の宣言は外から直接見える。
- 廃棄された分岐(discarded branch)については構文解析すら実施しない (字句解析だけ行う)。

一方で N3576 および N3613 において静的な条件分岐の機能は厳しい批判に晒されることになる
一方で N3576 および N3613 において静的な条件分岐の提案は厳しい批判に晒されることになる
N3576 では Concepts Lite による条件付きの宣言を行う機能と、静的な条件分岐の機能の棲み分けが懸念された。
両機能の矛盾が生じる懸念から少なくとも Concepts Lite の仕様が固まるまでは静的な条件分岐の議論は凍結するべきとの意見が強かった。
更に、N3613ではN3322/N3329で提案された仕様に対する批判が行われた
更に、N3613 では N3322/N3329 で提案された仕様に対する批判が行われた
分かりにくくメンテナンスしにくいという事と、Concepts Lite との棲み分けの問題の他に、
AST(抽象構文木)を元にしたソースコードの静的解析ツールの開発を困難にするとの指摘があった。
また、静的な条件分岐で記述が本当に簡単になるのかという点についても疑問を呈した
クラスメンバに対する静的な条件分岐に関しては、使用する側でも静的な条件分岐が毎回必要になるので
実際のところ不便なのではないかという事、
実際に複雑な処理を実装するのはライブラリ実装者であり、
その様な者は従来の複雑な手法も理解していなければならないという事などが挙げられた
また、静的な条件分岐で記述が本当に簡単になるのかという点についての幾つかの疑問も呈された
例えば、クラスメンバに対する静的な条件分岐に関しては
使用する側でも同様の静的な条件分岐が毎回必要であり煩雑であるということ。
また、実際に複雑な処理を実装するのはライブラリ実装者であり、
その様な者は従来の複雑な手法も理解しているはずなので、新しい機能は不要ではないかということ
他に、関数の多重定義や従来のテンプレート特殊化・SFINAE技法と比べて自由度が小さいということ、
更にそれらとの組み合わせよって起こる問題についても懸念があった。

N4461, P0128R0, P0128R1 では批判を受けて静的な条件分岐の大幅な単純化が提案された。
特に、静的な条件分岐は上記 (C) ブロックスコープに限定し、宣言の条件分岐には使えないこととした。
また静的な条件分岐は通常の`if`文と同様に変数のスコープを作成するということ、
及び、廃棄された分岐の構文解析もテンプレートの2段階名前探索と同様にして実施するということが提案された。
また、テンプレートの中でしか静的な条件分岐は使えないということも提案された
また、静的な条件分岐はテンプレートの中でしか使えないよう制限することも提案された

P0292R0-P0292R2 では、静的な条件分岐のキーワードが `if constexpr` になった。
また、`static_assert` と同様に、テンプレートの外でも静的な条件分岐を許すように修正された。
`auto`による関数の戻り値の型の推論で、廃棄された分岐内の`return`文は参考にしない旨が明記され、
C++17の規格原案N4606において変更が適用された
C++17 の規格原案 N4606 において変更が適用された

静的な条件分岐の提案に関連する文書:

Expand All @@ -311,14 +311,14 @@ C++17の規格原案N4606において変更が適用された。

N3322 では `static_assert` からの連想でキーワードとして `static_if` / `else` の組が提案された。
N3329 ではD言語を参考にして `static if` / `else` の組が提案された。
N3613 では `static if` の様な複合キーワードは間にコメントを挟めるのは分かりにくく問題であると指摘された
また、通常の`if`文と静的な条件分岐とが入れ子になっている時に`else`がどれに属しているのか分かりにくいとの指摘もあった。
N3613 では `static if` の様な複合キーワードは、間にコメントを挟めるので、分かりにくく問題であると指摘された
また、通常の`if`文と静的な条件分岐とが互いに入れ子になっている時に `else` がどれに属しているのか分かりにくいとの指摘もあった。
これを受けて P0128R0 では、静的な条件分岐がブロックスコープに制限されると共に、`constexpr_if` / `constexpr_else` となった。
P0128R1 では、`constexpr if` / `constexpr_else` に改訂された。
P0292R0 で現行の `if constexpr` / `else` が提案され、
文法上は通常の`if`文に対する`constexpr`キーワードの修飾という形にまとめられた。
`else` に関しては、通常の入れ子の`if`文と同様に一番近くの`if`/`if constexpr`文に属するとすれば曖昧さはないこと、
また `if constexpr` を繋げた時の煩雑さから単に `else` とすることになった。
また `if constexpr` を繋げた時の煩雑さから単に`else`とすることになった。

```cpp
// N3322
Expand Down Expand Up @@ -354,10 +354,11 @@ constexpr_else
```
静的な条件分岐の各分岐を囲む波括弧 `{ ... }` に関しては、
廃棄された分岐の構文解析を行わないN3329においては必須とされた
廃棄された分岐の構文解析を行わない N3329 においては必須とされた
つまり、構文解析は行わずに単に括弧だけの対応を取ることにより分岐の終わりを調べる。
しかし、N3613における批判により、結局はテンプレートの2段階名前探索と同様に、
しかし、N3613 における批判により、結局はテンプレートの2段階名前探索と同様に、
廃棄された分岐でも構文解析は実施され、非依存名に関しては1段階目で検証されることとなった。
これにより通常の`if`文と同様に `{ ... }` は任意となった。
```cpp
// N3329
Expand All @@ -376,7 +377,7 @@ void g() {
}
```

静的な条件によって関数の宣言・実装を切り替える構文として、N3322およびN3329では以下のようなものも提案された
静的な条件によって関数の宣言・実装を切り替える構文として、N3322 および N3329 では以下のようなものも提案された
これは `requires` キーワードを用いる Concepts Lite が目的とする機能との類似性もあり、
Concepts Lite の仕様が確定していない段階で、
どのように棲み分けるのかや両方用いた時の振る舞いについての考察が問題になった。
Expand All @@ -403,7 +404,7 @@ void f()
```
ブロックスコープでの静的な条件分岐について、
ライブラリによる代替手段として以下のようなものも可能であることがP0128R0で指摘されている
ライブラリによる代替手段として以下のようなものも可能であることが P0128R0 で指摘されている
つまり、ジェネリックラムダの実体化は実際に関数の呼び出しがある時に行われるので、
実体化を遅延することができるのである。
Expand All @@ -417,7 +418,7 @@ template <int arg, typename ... Args> int do_something(Args... args) {
```

その他に、元々静的な条件分岐で置き換える目的だった、
旧来のテンプレート特殊化・SFINAE・タグディスパッチ・EBO・再帰的な継承などの技法を用いた複雑な代替手段もあるが
旧来のテンプレート特殊化・SFINAE・タグディスパッチ・EBO・再帰的な派生などの技法を用いた複雑な代替手段もあるが
それらを一つ一つここで紹介することは避ける。

## 関連項目
Expand Down

0 comments on commit 08a8015

Please sign in to comment.