What’s New In C++14 言語仕様

このノートでは C++14 で注目すべき言語仕様を学習する。すでに cpprefjp がそのへんをきれいに整理している。それを利用して、読みながら急所を記していくことにする。

タイピングの都合で訳語は cpprefjp のものと一部変更して記す。

言語機能

C++14 における言語機能の追加および変更は C++11 からのマイナーバージョンアップという程度に留まっている。

二進数リテラル

リテラル整数に prefix として 0b または 0B を付すと二進数として扱われる。

  • Python と同様。C/C++ にも存在して然るべき記法だ。

  • 当然ながら数値部分は 0 または 1 で尽くされる。

関数の戻り値に対する型推論

〈関数宣言の構文において、先頭の戻り値型を auto もしくは decltype(auto) とすることで、戻り値の型が関数の return 文から推論される〉機能が追加された。

コーディングが楽になる機能追加だ。

  • 関数宣言構文はフリー関数だけでなく、非仮想メンバー関数に対しても使用できる。

  • 先行宣言をする場合、その関数を使用するコードから関数の定義が見える必要がある。

  • 複数の return 文がある場合には、それらの式に共通する型が採用される。それらがバラバラである場合はコンパイルエラーだろう。

  • 戻り値の型を推論する関数宣言構文において、{} で囲まれた初期化子リストは return 文で返せない。

型推論 decltype(auto)

decltype に与える式を右辺の式で置き換えて型推論できるようになった。 cpprefjp の例を引用する。

int a = 3;
int b = 2;
decltype(auto) d = a + b;

この decltype(auto)decltype(a + b) と置換されて、したがって int だと推論される。

decltype(auto) は、戻り値として変数への参照を返したい場合に使用する。

注意したいのは次のようなケース。上記の機能により f の戻り値の型には auto と書くことも考えられる。その場合は int 型関数として決定される。他方で decltype(auto) とすると関数 fint& 型となる。

decltype(auto) f(int& r)
{
    return r;
}

後置戻り値型をプレースホルダーにしてよい

次の二種類の構文において -> の次に auto を書けるようになった。いつものように必要に応じて const/volatile, *, &, && を付けてもよい。

  • 戻り値の型を後置する関数宣言構文

  • ラムダ式

ラムダ式の初期化捕獲

この機能はラムダ式の [] の部分に任意の式(の結果)を書けるというものだ。この角括弧で変数宣言と定義を同時にできると言っている。

汎用ラムダ

C++11 のラムダ式が拡張されて、パラメーターにテンプレートを使用できるようになった。ただしその構文はキーワード template ではなく auto を用いる:

auto plus = [](auto a, auto b) { return a + b; };

このラムダ式は意味としては次の関数テンプレートと同様だ:

template <class T1, class T2>
auto operator()(T1 a, T2 b) const
{
    return a + b;
}
  • いつものように必要に応じて const/volatile, *, &, && を付けてもよい。

  • 関数パラメーターパック ... を併用してもよい。

  • 捕獲を含まない汎用ラムダはシグニチャーさえ一致すれば関数ポインターへ変換される。

変数テンプレート

関数テンプレート、クラステンプレートの類比で、変数(というか定数)もテンプレート化することができるようになった。 cpprefjp の例を引用する:

template <class T>
constexpr T pi = static_cast<T>(3.14159265358979323846);

これにより、円周率を参照する箇所で例えば pi<double> のように書ける。

  • cpprefjp のサンプルコードが素晴らしい。一度はコンパイルして実行するといい。

キーワード constexpr の制約が緩くなった

C++11 で導入されたこのキーワードは、C++14 で次のように適用範囲が広がる。型が constexpr である関数の定義において:

  • ふつうの変数宣言が許される。

  • if 文と switch 文を書くことが許される。

  • ループ文の存在が許される。

    • goto は許されない。

  • 変数の書き換えが許される。

    • ローカル変数を変更するコードが許される。

    • その関数が所属するクラスの非静的メンバー変数の書き換えが許される。

  • 戻り値型として void が許される。

    要するに constexpr void 型関数が書けるということだが、何の意味があるのかは知らない。

そして、constexpr 非静的メンバー関数に付く暗黙の const 修飾が C++14 で廃止される。これからは const にも 非 const にも書ける。

宣言時のメンバー初期化を持つ型に対して集成体初期化を許可

またしても cpprefjp の例を引用する。C++11 では次のように括弧を二度書くところを:

std::array<int, 3> ar = {{ 1, 2, 3 }};

C++14 では括弧を省略しても許される:

std::array<int, 3> ar = { 1, 2, 3 };

省略されているのは内側の括弧だそうだ。

属性 [[deprecated]]

ライブラリー作者向けの機能だろう。提供している関数やクラスの一部が最新版で deprecated になったことを利用者に確実に知らせたい。そこで、対象となる関数やクラスにこの属性をマークすることで、コンパイラーがその旨を通知するということを期待される機能だ。

これは実際に利用するときになって仕様を確認するほうがいいだろう。

数値リテラルの桁区切り文字

Python などではアンダースコアを用いるところを C++ ではシングルクォーテーションを用いる。というか、アンダースコアを使いたくても既存機能(ユーザー定義リテラルでアンダースコアから始まる数値だけからなる suffix を定義できる)とバッティングするので使えなかったようだ。

サイズ付きメモリー領域割当解除

以下の演算子 operator delete, operator delete[] のオーバーロードが許される:

void operator delete(void* ptr, std::size_t size) noexcept;
void operator delete(void* ptr, std::size_t size,
                     const std::nothrow_t&) noexcept;

void operator delete[](void* ptr, std::size_t size) noexcept;
void operator delete[](void* ptr, std::size_t size,
                       const std::nothrow_t&) noexcept;
  • サイズ付き operator new, operator new[] に対応するということはわかるだろう。

  • クラス版ではなくフリー版というところがミソか。

定義済みマクロ

マクロ __cplusplus の値が更新されて 201402L となる。

機能テストマクロ

g++ (GCC) 10.0.2 ではすべて定義されていることを確認。使わないから覚えなくていい。

リテラル演算子宣言時の空白文字は省略してよい

下の行の記法でも許されるようになった。

return_type operator"" _xyz(const char*){ ... }
return_type operator""_xyz(const char*){ ... }

使わないから覚えなくていい。

nullptr_r の定数式を非型テンプレートパラメータとしてよい

テンプレートリストの非型テンプレート引数として、つまり class でも typename でもないようなテンプレート引数として、型 nullptr_t を書くことが許される。

template <typename C, typename V, std::nullptr_t P>
class MyClass{
    ...
};