読者です 読者をやめる 読者になる 読者になる

ボレロ村上 - ENiyGmaA Code

中3女子です。

次期 C++ ではポインタ演算は定数式になるようだ/次期 C++ の constexpr 化を考える

    • §5.19 Constant expressions (N3290 より抜粋)

2 A conditional-expression is a core constant expression unless it involves one of the following as a potentially
evaluated subexpression (3.2), but subexpressions of logical AND (5.14), logical OR (5.15), and conditional
(5.16) operations that are not evaluated are not considered [ Note: An overloaded operator invokes a
function.-end note ]:
(中略)

  • a subtraction (5.7) where both operands are pointers;


C++11 の規格では、上記にあるようにポインタ同士の減算は非定数式であると明示されています。
この文面は C++11 の規格制定以降に出された N3337 でも同様です。


ところが、現時点で最新である N3376 ではこの文言が削除されています。
N3376 の §5.19 Constant expressions においては、非定数式としてポインタが言及されているのは、演算が undefined behavior になる場合のみです。


ポインタ演算が非定数式であるという制限のため、Sprout でもコンテナのイテレータや distance の実装において、かなり面倒な回避策をとらざるをえませんでした。
(例えばポインタでなくコンテナへの参照とインデックスを保持するイテレータであるとか、ポインタの distance を減算でなく線形探索で求めたりとか)


個人的には、この点だけでも次期C++に期待が高まります。


ついでに標準ライブラリの constexpr 化も期待したいところです。
特に std::forward など「想定される実装を変えずに constexpr を付けられる」ところはすべきだと思うのですが、
C++11 時点での constexpr を付ける方針が「文脈によらず定数式になれることが自明である」部分であったので、
(例えば std::forward は T がリテラル型であれば定数式になれるが、非リテラル型ではそうでない)
また constexpr 化を進めるとすれば、新たな指針が必要となるでしょう。


これについては、大まかに2通りの方針が考えられます。
  1.想定される実装を変えずに constexpr を付けられる。
  2.インタフェースを変えずに constexpr を付けられる。


1.については、上で挙げた std::forward などは、想定される実装が単純なキャストであるので、高々1行の return 文で書ける。
これはもちろん constexpr 関数の要件を満たすので、単に constexpr を付けるだけでよい。


2.については、例えばハッシュ関数や数学関数などはインタフェースを変えずに constexpr を付けられる。
しかしながら、それらに constexpr を付けるということは、想定される実装に大きな制限を加えるということだ。
例えば開発者がデバッグのために古典的な実行時 assert を仕込むことも出来ない。
これについては、中3女子である自分も、標準に強く勧められるべきものではないと思います。


何にせよ次期 C++ では、地味ながら有用な改善が加えられてゆくものと考えられます。
もちろん constexpr も例外ではないだろうので、みなさんもっと constexpr プログラミングを愉しみましょう。



(追記)
なお、聞くところによると clang3.1 では既にポインタ演算の制限が取り払われているらしいです。
素晴らしい。
clang はもはや GCC を越えた。
自分の環境では clang でコンパイルしようとすると何故か GCC4.7 のヘッダでエラーになって死ぬのが悔やまれる。