C++14に入ることが決まったものの一覧(みたいなもの) を読んで気になった C++14 での変更のうち,コア言語の中から気になったものを挙げてみる.
ただし,
- 動的配列
- 多相ラムダ
- 変数テンプレート
等に関しては, http://cpplover.blogspot.jp/2013/04/bristolc14.html で説明されているので,省略させて頂いた.
一般関数の型推論
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3664.html
素晴らしい変更.
これによりラムダ式における型推論が一般の関数に対しても可能になる.
つまり,今まで
template<class F, class... Args> auto apply( F f, Args&&... args ) -> decltype( f( std::forward<Args>(args)... ) ) { return f( std::forward<Args>(args)... ); }
と decltype
を使って書いていたのが,
template<class F, class... Args> auto apply( F f, Args&&... args ) { return f( std::forward<Args>(args)... ); }
と書けばそれで良くなる,ということだ.*1
また,それと同時に,ラムダ式も含め,型推論できる関数の制約が大幅に緩和されている.
C++11 では
auto f = [](int x, int y) { return x < y ? y : x; };
のように,単一の return 文のみを含む関数でなければ型推論が行えなかったのに対し,
C++14 では
auto f = [](int x, int y) { if( x < y ) { return y; } else { return x; } }
のように書いた場合でも推論してくれる.
それどころか,
auto hoge = []{ struct Internal { // なんか実装する }; return Internal{}; }();
のように,ローカルクラスを返すラムダが作れるようになる.
ラムダに対する move capture (Generalized Lambda-capture)
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3648.html
ねんがんの move capture を てにいれたぞ!
これにより,
auto p = std::make_unique<Hoge>(); // make_unique も C++14 で導入される auto f = [p = std::move(p)] { return p->do_something(); };
のように, move しか出来ないクラスをラムダにキャプチャさせることが可能になる.
更に嬉しいことに,この機能は move capture 以外でも使える.
int x = 1; auto f = [x=x+1] (int y) { return x + y; };
キャプチャされた変数の名前として, x という「キャプチャ元と同じ名前」を使える点にも注目してほしい.
また,参照キャプチャもできる.
std::array<int, 5> a; auto f = [&r = a[1]] (int x) { r = x + 1; };
非常に便利.
constexpr 大幅強化
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3652.html
凄い. とにかく凄い.
if が使えるようになったぜヒャッハー,とか,そういうレベルじゃない.
constexpr int next(int x) { return ++x; // 副作用! }
のような関数も問題なくコンパイル時に計算できる. すごすぎる.
new におけるメモリ確保を省略可能に
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3664.html
これも凄い変更. いずれ来るとは思っていたが,まさか C++14 で来るとは.
この変更により,コンパイラは new の際のメモリ確保を省略するような最適化を行なってよいことになる.
つまり,ただでさえ高速な std::string や std::vector が,更に高速になる可能性がある*2,ということだ.
std::string や std::vector に関しては現行規格でもメモリ確保は省略できるようになっている*3のだが,
この変更により,普通の new に対しても その手の最適化が出来るようになるし,
それが切っ掛けとなり std::string や std::vector といったクラスの最適化が行われるようになれば,正に御の字である.
また,この方針を突き詰めていくと,いずれは constexpr new にも到達するであろうことも考えると,
単純な変更のようでいて,実際には非常に夢の広がる変更であるといえるだろう.
range-based for の ADL ルールの変更
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3539.html#1442
range-based for で ADL 経由で begin/end を呼ぶ場合の名前検索ルールの変更.
ADL は邪悪だが,時には必要になる. かもしれない.
具体的には, C++11 の range-based for では, ADL の際に std 名前空間も考慮していたのが,
今回の変更で, std 名前空間は,元から std 名前空間を考慮するような場合を除き,考慮に入れなくなった.
更に, begin/end を ADL で「のみ」検索する(現在のスコープは無視する)旨が,規格に明記された.*4
とはいえ, ADL は邪悪なので,基本的にはメンバ関数 begin/end を定義して使うようにするほうがいいだろう.
具体的な変更としては,
- std からは begin/end を検索しない
- 現在のスコープからは begin/end を検索しない
となる.
後者の変更は若干厄介で,
using ns::begin; using ns::end; for( auto x : xs ) { // 処理 }
というコードがあったとき,この変更が原因でコードが動かなくなる可能性が出てくる.
ともあれ,普通は ADL という邪悪な方法ではなく,メンバ関数の begin/end を使うべきであるので,
この変更が実際のコードに与える影響は少ないだろう.
関数の noexcept 修飾の内部で使われている式の実行を遅延するように
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3539.html#1330
これにより noexcept 節中で SFINAE を行なうことが明確に不可能になる.
void g(std::string s); // #1 template<class T> void f(T x) noexcept(noexcept(g(x))); // #2 void f(...); int main() { f(0); // calls #1 in C++14 }
とはいえ, noexcept 中での SFINAE は最初からグレーゾーンだったので,
C++11 であっても使うべきではないのは確かだ.
deprecated の定義の変更
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3539.html#223
removal という言葉が使われることで,より分かりやすくなった.
良い変更だと思う.
SFINAE ルールの厳密化
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3539.html#1462
"ill-formed; no diagnostic is required" とか書かれていた場合には SFINAE は発動しないことが明記.
C++によほど精通した人でない限りは SFINAE は使うべきではないと思う.
暗黙のうちに削除された move ctor が copy ctor を覆い隠してしまう問題への対応
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3667.html
いわゆる一つのバグ対応.
細かい変更なので C++ マニアでもなければ気にする必要はない.
追記
この記事で説明しなかった C++14 の機能に関しては,
http://isocpp.org/blog/2013/05/post-bristol-standards-papers-mailing-available
で Adopted となったものを確認すれば把握できる.
個々の paper の解説は
http://cpplover.blogspot.jp/2013/05/2013-05-post-bristol-mailing.html
を読むとよい.
*1:ただし,後者の場合には SFINAE は行われない,戻り値は自動で参照が消える,といった差はあるので,前者と後者は厳密には等しくない.
*2:ただし現行の規格のままでは無理 https://twitter.com/k_satoda/status/332533786308902914