C++17つまみぐい

この記事は、[C++ Advent Calendar 2016](http://www.adventar.org/calendars/1527)の17日目です。

こんにちは。聖夜も来週に迫ってまいりました今日このごろ、皆様に置かれましてはどうお過ごしでしょうか。私はAdCの締切に追われています。

さて、先日もアドベントカレンダーに一本記事を書かせていただきましたが、今回は初心者向けカレンダーではないので初心者向けではない内容を書こうと思います。
いよいよ来年に迫ってきたC++17、欲しかった機能が規格に盛り込まれたかどうかは分かりませんが、コンパイラーの実装も結構進んできて、"-std=c++1z"オプションを使えば試せるものも増えてきました。

この記事ではそんな新機能のうち、**コア言語機能で、gcc/clangどちらかのコンパイラーに実装されているもの**を試していこうと思います。
どれが実装されているかとか自分で調べるのは面倒なので、[こちら](http://en.cppreference.com/w/cpp/compiler_support)を参照したいと思います。各コンパイラーの対応状況がひと目で分かって便利ですね。

動作確認には[wandbox](http://melpon.org/wandbox/)の"gcc HEAD"もしくは"clang HEAD"を利用します。この記事を書いている時点では

* gcc HEAD 7.0.0 20161216 (experimental)
* clang HEAD 4.0.0 (trunk 289950)

となっていますが、おそらく数日で変わるので、後々までサンプルコードが動作する保証はできません。
というか、既に昨日今日あたりでgccが変わってるよ！

そんなわけで、しばらくの間、お付き合いください

# [N3922 New auto rules for direct-list-initialization](http://wg21.link/n3922)

[動作確認コード](http://melpon.org/wandbox/permlink/KOv4aDVgDIP2oaMJ)

C++14までの規格では、`auto`変数のリスト初期化時の挙動は、`std::initializer_list`になるというものでした。
例えば、

```cpp
auto a{0}; // std::initializer_list<int>
auto b{'a', 'b', 'c'}; // std::initializer_list<char>
```
などです。
しかし、実際にautoで型推論を行う場合、`std::initializer_list`に推論してほしい場面はあまりありません。普通は

```cpp
auto a{0};
```
と書かれていれば`int`型になってもらいたいのです。

という訳で、挙動が修正されました。**1要素のリスト初期化はリストの中身の型に推論**されるようになります。また、**2要素以上のリスト初期化は不可能**になりました。

```cpp
auto a{0}; // int
auto b{'a', 'b', 'c'}; // ill-formed
```

`std::initializer_list`に推論したい場合は、コピー初期化構文を使うと可能です。逆に、

```cpp
auto a{0};
auto b = {0};
```
この2つは挙動が異なるので注意しましょう。

なお、gccでもclangでも、"-std=c++1z"を付けなくてもこの挙動になっています。確かめていませんが、MSVCでさえ14.0でそうなっているようです。ある意味規格違反と言えなくもありませんが、C++14の策定に間に合わなかった規格のバグ修正をコンパイラーが先取りしたようなものでしょう、多分。

# [N3928 static_assert with no message](http://wg21.link/n3928)

[動作確認コード](http://melpon.org/wandbox/permlink/ZwsiRVWAOXkXEPG4)

メッセージなしの`static_assert`が書けるようになりました。
……他に何を説明すれば良いのでしょう。多分みんな知ってるし、ようやくかよって思ってるよね。以上。

# [N4051 typename in a template template parameter](http://wg21.link/n4051)

[動作確認コード](http://melpon.org/wandbox/permlink/w10GNgqxx4atzhBy)

テンプレートテンプレートパラメーターに`typename`キーワードが使えるようになりました。
つまり、テンプレート引数リスト内で`typename`キーワードと`class`キーワードが同じ意味になります。
これも以前から利用可能な機能ではあるので、今更と言えば今更ですね

# [N4086 Removing trigraphs](http://wg21.link/n4086)

[動作確認コード](http://melpon.org/wandbox/permlink/AXk28L7KDbKA2NkQ)

トライグラフは死んだんだ。もう二度と戻っては来ないんだ。
良かったですね。

ただし、gccでは"-Wno-trigraphs"のオプションを付けておかないと、トライグラフに該当する文字列が出現した時に警告が出ます。負の遺産です。

# [N4230 Nested namespace definition](http://wg21.link/n4230)

[動作確認コード](http://melpon.org/wandbox/permlink/OsguStLm8tf0nbLH)

```cpp
namespace a { namespace b { namespace c { } } }
```
と書いていたのが、

```cpp
namespace a::b::c {}
```
と書けるようになります。小粒ですが嬉しい機能ですね

# [N4266 Attributes for namespaces and enumerators](http://wg21.link/n4266)

[動作確認コード](http://melpon.org/wandbox/permlink/EA0jUNCcQRFKjs56)

名前空間と列挙型、列挙子にattributeを付けることができます。
動作確認コードでは、とりあえず`[[deprecated]]`属性を付けてみました。gccでは属性を付けること自体はできたのですが、なぜか名前空間と列挙型の`[[depreceted]]`属性を認識してくれなかったのでclangで試しています。

これでいつでも、化石のようなコードの名前空間に`[[deprecated]]`を付けて回ることができるようになりますね。え？　名前空間に入ってない？

# [N4267 u8 character literals](http://wg21.link/n4267)

[動作確認コード](http://melpon.org/wandbox/permlink/yuI6oGaBI45U4BLo)

`u8`プレフィックスが文字リテラルにも適用できるようになります。
ただし、UTF-8にエンコーディングされたUnicode文字は1〜4バイトなので、2バイト以上の文字はchar型で表せません。という訳で実質U+007F以下の文字しか使えません。
エンコーディングが固定されるので、`u8`プレフィックスのつかない文字リテラルと違って環境依存性はなくなります。

# [N4268 Allow constant evaluation for all non-type template arguments](http://wg21.link/n4268)

[動作確認コード](http://melpon.org/wandbox/permlink/vTJZiU8sg9AqoEer)

非型テンプレートパラメーターでポインターや参照を使う場合、いろいろと制約があるのですが、その一部が緩和されます。
具体的には、**定数式で得た参照やポインターが、リンケージを持った変数の参照やポインターであれば、非型テンプレートパラメーターにできる**ようになります。
従って、

* リンケージを持った参照を返すconstexpr関数
* constexprに構築可能な一時オブジェクトのstaticメンバ変数

などを非型テンプレートパラメーターに使うことができます。
リンケージが必要なので、配列の要素やstaticでないメンバ変数などはこれまでと同様使えません。

```cpp
constexpr int& f(int& v) { return v; }
int a;
int b[1];
struct C { int i; static int j; } c;
template<int&> class X {};

X<f(a)> x1; //ok
X<f(b[1])> x2; //ill-formed
X<f(c.i)> x3; //ill-formed
X<f(c.j)> x4; //ok
```

# [N4295 Fold Expressions](http://wg21.link/n4295)

[動作確認コード](http://melpon.org/wandbox/permlink/s29z6O2AbR4PoxgR)

パラメーターパックをfold式で展開できるようになります。これにより、何らかの関数呼び出しなどを通さないと扱いづらかったパラメーターパックが扱いやすくなりました。

fold式には4種類があります。
以下の擬似コードで、Eは式、opは演算子、Pはパラメーターパックを含んだ式を表すこととします。
一つのfold式中に出てくるopは同じ演算子を表します。
fold式では、括弧は必須です。

```cpp:擬似コード
(E op ... op P) // binary left fold
(P op ... op E) // binary right fold
(... op P) // unary left fold
(P op ...) // unary right fold
```

それぞれのfold式は、以下のように展開されます。P1, P2, ..., Pnは、N個のパラメーターパックのそれぞれの要素を含んだ式です。

```cpp:擬似コード
(((E op P1) op P2) op ...) op Pn
P1 op (P2 op (... op (Pn op E)))
(((P1 op P2) op P3) op ...) op Pn
P1 op (P2 op (... op (Pn-1 op Pn)))
```

# [P0001R1 Remove Deprecated Use of the register Keyword](http://wg21.link/p0001r1)

[動作確認コード](http://melpon.org/wandbox/permlink/8UcWZoFrrdW9zLLA)

`register` 記憶域指定子がついに取り除かれます。最適化はコンパイラーに任せましょう。
ただし、`register`は予約語のままなので、識別子等に使うことはできません。

# [P0002R1 Remove Deprecated operator++(bool)](http://wg21.link/p0002r1)

[動作確認コード](http://melpon.org/wandbox/permlink/U4iDh96728iE11us)

bool型に対するoperator ++が取り除かれます。
元々C++98でdeprecatedになっていたものなので、取り除くには十分な時間が経った、ということのようです。

# [P0003R5 Removing Deprecated Exception Specifications from C++17](http://wg21.link/p0003r5)

[動作確認コード](http://melpon.org/wandbox/permlink/2bs32tDOZJTTFhBU)

関数の例外指定で何らかの型を指定することができなくなりました。`throw()`だけは引き続き使用可能ですが、`noexcept`と同じ意味なので`noexcept`を使用するべきです。

# [P0012R1 Make exception specifications part of the type system](http://wg21.link/p0012r1)

[動作確認コード](http://melpon.org/wandbox/permlink/6JOsaqnPVguD3l15)
例外指定が型システムに組み込まれることになりました。今までの方式での関数ポインタは全て、`noexcept(false)`されたものと同じ型になります。
互換性のため、`noexcept`指定された型から`noexcept(false)`指定された型へは暗黙的にキャストされますが、逆はキャストされません。

ところで、論文を見ると、 `throw(int)`みたいな記述が散見できるのですが、P0003R5に従うなら修正が必要そうです。

# [P0017R1 Aggregate initialization of classes with base classes](http://wg21.link/p0017r1)

[動作確認コード](http://melpon.org/wandbox/permlink/pc24Kmfd4sPGwBUN)

publicな基底クラスを持つ型のアグリゲート初期化ができるようになりました。基底クラスのそれぞれの型が、メンバ変数の先頭にあるのと同じ構文で初期化をできます。

# [P0018R3 Lambda capture of *this](http://wg21.link/p0018r3)

[動作確認コード](http://melpon.org/wandbox/permlink/Vm8ow0nFUNL0Mh4F)

従来のラムダ式では、`=`でキャプチャー指定しても`this`をコピーすることしかできませんでした。`this`はポインターなので、コピーされてもメンバーまでコピーすることはできません。
コピーキャプチャーしたつもりで、メンバー変数にアクセスすると既に破棄されていたなんてこともあり得ました。
そこで、`*this`をキャプチャーすると、明示的にコピーを行うことができるようになりました。

# [P0028R4 Using attribute namespaces without repetition](http://wg21.link/p0028r4)

[動作確認コード](http://melpon.org/wandbox/permlink/c9yyTyvOcUHB1nbJ)

attributeには、ベンダー固有の名前空間を使って、コンパイラー独自の機能を追加できることになっています。コンパイラー独自のattributeは、`名前空間名::属性名`といった構文を使って、つまり普通の名前空間解決と同じような書き方をします。
例えばgccなら、これから先`[[gcc::foo]]`と言った属性が独自機能として用意される可能性があります。

属性は複数同時に指定できるのですが、名前空間に入った属性を複数指定する時に、`using`を使って名前空間を指定できるようになっています。
例えば、`[[using CC: foo, bar]]`と指定すると、`[[CC::foo, CC::bar]]`と指定したのと同じことになります。

今のところどんなコンパイラー固有の属性があるのかよく把握していませんが、`[[gcc::fallthrough]]`という属性は存在しているようなので、それで確かめてみました。
ただし、`[[fallthrough]]`属性はベンダー独自機能ではなくC++17の標準規格として採用予定です。

# [P0035R4 Dynamic memory allocation for over-aligned data](http://wg21.link/p0035r4)

[動作確認コード](http://melpon.org/wandbox/permlink/2NrFKcCyCDKh5q2s)

`new`演算子は、`std::max_align_t`以上のアライメントでメモリを確保します。つまり`max_align_t`を超えるアライメントを持つ(over-aligned)型の場合、正しいアライメントでメモリ確保が行われるとは限りません。これは、SIMD命令などを使う場合にしばしば問題になります[^1]。この間もこのアライメント問題を踏んだ人を観測しました。

[^1]: 例 https://eigen.tuxfamily.org/dox/group__TopicUnalignedArrayAssert.html

そこで、`new`演算子でover-alignedな型が確保された時は、`std::align_val_t`を受け取る`operator new`のオーバーロードが用意されることになりました。正しく実装されていれば、アライメントに合わせてメモリを確保してくれるようになります。
もちろん、他の`operator new `と同じように、ユーザー定義を行うことも可能です。

# [P0036R0 Unary fold expressions and empty parameter packs](http://wg21.link/p0036r0)

[動作確認コード](http://melpon.org/wandbox/permlink/zY0en8VYWqQxmPYl)

先ほども解説したfold式ですが、unary fold expressionの場合、一つ問題があります。

```cpp
template<typename ...Args>
auto sum(const Args& ...args) {
    return (args + ...);
}
```
例えば上記の式ですが、`sum(1, 2, 3)`と呼び出した場合、`1 + 2 + 3`の結果が帰ります。
`sum(42)`とした場合は、`42`が帰ります。
では、`sum()`を呼び出した場合は？
unary fold expressionは、パラメーターパックが0個の場合、何を返せば良いのでしょうか。

この問題を解決するため、ある種の演算子に対しては、パラメーターパックが空だった場合のデフォルト値を決めよう、という提案です。

今のところ、`&&`演算子に対しては`true`が、`||`演算子に対しては`false`が、`,`演算子に対しては`void()`がデフォルト値になっています。

ところで、例に出した`sum`のように`+`演算子を使う場合と、`*`演算子を使う場合に関しては当初、それぞれ`0`および`1`をデフォルト値にしようという提案だったようですが、これらの演算子に関してはデフォルト値は却下されたようです。
つまり、上記の`sum`関数に関しては、binary fold expressionにするか、特殊化やオーバーロードでうまいことやるしかないようです。

# [P0061R1 __has_include in preprocessor conditionals](http://wg21.link/p0061R1)

[動作確認コード](http://melpon.org/wandbox/permlink/XFhCl0BIMKe0oxnZ)

プリプロセス時条件式に`__has_include`というものが追加されました。
効果は、指定した名前のファイルが存在するかどうかを調べるものです。`__has_include("filepath")`もしくは`__has_include(<filepath>)`という構文です。ファイルパスの探索方法は`#include`ディレクティブと同じです。

# [P0091R3 Template argument deduction for class templates](http://wg21.link/p0091r3)

[動作確認コード](http://melpon.org/wandbox/permlink/krPrfYsgqrneuF6a)

テンプレートクラスの初期化式で、クラスのテンプレート引数を推論できるようになります。
何もしなくても推論してくれる場合と、推論のための新しい構文を使って推論できるようにする場合がありますが、これにちゃんと対応してくれれば、標準ライブラリのコンテナをテンプレート引数なしで初期化できるようになるはずです。

現状、コア言語機能としては対応されていますが、ライブラリの方はまだ対応できていない部分も多そうです。

# [P0127R2 Non-type template parameters with auto type](http://wg21.link/p0127r2)

[動作確認コード](http://melpon.org/wandbox/permlink/yu44qutq8lS9IgW1)

非型テンプレート引数の型名に`auto`が使用できるようになります。ラムダ式の`auto`引数と同じような感じで推論されます。
今まで型名を別に指定しなければならなかったところで、型名を指定しなくても良くなるのではないかと期待しています。

なお、gccは現状`auto`を使用したテンプレートクラスの部分特殊化を行うと、インスタンス化の際にinternal compiler errorが発生するようです。この文章を公開したらバグレポを出そうと思います。

# to be continued ...

まだ全部見きれてないのですが、ちょっと変更点の多い論文を理解するのに時間がかかっているので、とりあえずここまでとしようと思います。
`constexpr if`など試したいものもまだあるのですが、近いうちに更新します、ということにさせてください。


明日は、ignis_fatuusさんの記事です。