私はずっと C++98/C++03 の環境で仕事をしていたのですが、数年前ぐらいからやっと C++11 を使い始めました。
ということで、以下のサイトをベースに、C++11 の新機能をまとめます。
内容はかなり浅いです。
概要だけ知りたいという人向けです。
細かいものを含めると新機能が膨大にあるので、私の独断で一部のみをピックアップしました。
今回はライブラリー以外の機能をまとめます。
ライブラリーの新機能に関しては後編でまとめる予定です。
一般的な機能
auto
値から型を推論します(型推論)。
推論するために、宣言時に必ず初期化しなければなりません。
auto a = 0; // a は int
auto b = 0.0; // b は double
auto c; // 型が推論できないのでコンパイルエラー
decltype
変数から型を取得する機能です。
int a = 0;
decltype(a) b; // b はint型
範囲for文
配列やコンテナの中身をfor文で回すための表現がシンプルになりました。
auto
を使います。
// C++03まで:
std::vector<int> v = ...;
std::vector<int>::const_iterator e = v.end();
for ( std::vector<int>::const_iterator it = v.begin(); it != e; ++it ) {
// *it でアクセス可能
}
// C++11以降:
std::vector<int> v = ...;
for ( const auto& i : v ) {
// i でアクセス可能
}
初期化子リスト / 一様初期化
vector
等のコンテナを波カッコで初期化できるようになりました。
// = は有っても無くてもOK
std::vector<int> v1 = { 0, 1, 2 };
std::vector<int> v2{ 10, 11, 12 };
std::initializer_list
を使えば、独自クラスも波カッコで初期化できます。
これに伴い(?)、波カッコは何でも初期化できるようになりました。
int a{ 0 }; // int a = 0; と同じ結果
int b = { 1 }; // = は有っても無くてもOK
SampleClass c{ "text" }; // SampleClass c( "text" ); と同じ結果
右辺値参照・ムーブセマンティクス
内容が難しいし、説明すると長くなるので省略。
頭の良い人が解説してくれてると思うので、それを参照してください。
ラムダ式
ラムダ式が C++ に導入されました。
(言語によっては「クロージャー」や「無名関数」とも呼ばれている機能です。)
[キャプチャリスト](引数) -> 戻り値 { 関数本体 }
というフォーマットで記述します(mutable
や例外、属性は省略)。
int x = 5;
auto multiply = [x](int value) -> int {
return ( value * x );
};
int a = multiply( 10 ); // a == 50
// キャプチャ不要なら "[]" 内は空で良い
// 戻り値が無ければ "-> 戻り値" を省略できる
auto print = [](int value) {
std::cout << value << std::endl;
};
print( 10 );
キャプチャにはコピーキャプチャ([x]
)と参照キャプチャ([&x]
)があり、1つ1つ指定しなくても全変数をキャプチャする方法(([=]
or [&]
)もあります。
[this]
で this をキャプチャすることで、ラムダ式の中でメンバ変数にアクセスすることも可能です。
noexcept
関数が例外を投げないことを明示します。
int getZero() noexcept {
return 0;
}
noexcept
指定して例外を投げてもコンパイルは通りますが、コンパイラは例外を投げない前提で最適化してしまうので、例外を投げると落ちる可能性があります。
constexpr
コンパイル時定数として扱います。
コンパイル時に決定しなければコンパイルエラーになります。
以下はコンパイル時に a が 20 になることが決まるので通ります。
constexpr int twice( int v ) {
return ( v * 2 );
}
constexpr int a = twice( 10 );
nullptr
ヌルポインタを表す新しいキーワードです。
C++ では NULL
は整数の 0
です。
#define NULL 0
NULL
が整数だと問題があったので、std::nullptr_t
型の nullptr
というキーワードができました。
使い方は NULL
と同じです。
int* p = nullptr;
if ( p ) {
...
}
ただし、NULL
のように整数としては扱えません。
int a = NULL; // NULL == 0 なのでコンパイルが通る
int b = nullptr; // nullptr は整数型ではないのでコンパイルが通らない
ユーザー定義リテラル
独自のリテラルサフィックスを定義することができます。
以下の場合、value
は 31.41592 になります。
long double operator"" _pi( long double d ) {
return ( d * 3.141592 );
}
auto value = 10.0_pi;
クラス関係の機能
移譲コンストラクタ
コンストラクタから同クラスの別コンストラクタを呼べるようになりました。
従来は以下のように共通の初期化関数を用意して、コンストラクタでそれを呼んでいました。
class Test {
public:
Test( int i ) {
initialize( i );
}
Test( const char* c ) {
initialize( atoi( c ) );
}
private:
void initialize( int i ) {
// 初期化
}
}
C++11からは以下のように別コンストラクタを呼べます。
class Test {
public:
Test( int i ) {
// 初期化
}
Test( const char* c ) : Test( atoi( c ) ) {
}
}
非静的メンバ変数の初期化
従来、メンバ変数の初期化は以下のようにして行なっていました。
class Test {
public:
Test();
int value;
std::string text;
};
Test::Test()
: value( 10 )
, text( "ten" ) {
}
C++11 以降は以下のように初期化できます。
class Test {
public:
Test();
int value = 10;
std::string text = "ten";
};
Test::Test() {
}
overrideとfinal
virtual
関数をオーバーライドする際、ケアレスミスは致命的です。
関数名を間違えたり、引数の const
を忘れたりするだけでオーバーライドできません。
class AAA {
public:
virtual void func() {
std::cout << "func of AAA" << std::endl;
}
};
class BBB : public AAA {
public:
// func を funk とタイポ
virtual void funk() {
std::cout << "func of BBB" << std::endl;
}
};
override
というキーワードを使うと、オーバーライドしていない場合にコンパイルエラーになります。
override
は関数名の後に書きます。
class BBB : public AAA {
public:
// コンパイルエラー
virtual void funk() override {
std::cout << "func of BBB" << std::endl;
}
};
同様に、クラス名の後か関数名の後に final
というキーワードを書くと、そのクラスから派生したり、その関数をオーバーライドしたりするとコンパイルエラーになります。
class AAA final {
};
// コンパイルエラー
class BBB : public AAA {
};
クラス以外の型に関する機能
スコープを持つ列挙型
enum class
を使えば、型チェックを厳格にすることができます。
enum class Color { Red, Green, Blue };
Color c1 = Color::Red;
Color c2 = Color::Red + 1; // コンパイルエラー(Color と int を演算しているため)
また、enum
の前方宣言が可能になりました。
スコープを持たないenum(従来のenum
)でも、スコープを持つenum(enum class
)でも可能です。
// define.h
// -----
// 基底型が必要
enum Color : int { /*略*/ };
// -----
// use.h
// -----
enum Color : int;
struct Test {
Test();
Color c;
};
// -----
// use.cpp
// -----
#include "define.h"
Test::Test()
{
c = Color::Red;
}
// -----
テンプレート関係の機能
エイリアステンプレート
従来、型の別名を定義する場合は typedef
を使っていました。
より文法が直感的な using
でも型の別名を定義できるようになりました。
「エイリアステンプレート」というタイトルですが、テンプレートで無くても使えます。
using MyInteger = int;
任意の式によるSFINAE
テンプレート引数の展開に失敗してもコンパイルエラーとならず、他の候補で解決を試みることが仕様になりました。
SFINAE(スフィネェ)と呼ばれているものです。
struct Test {
using Integer = int;
};
template <typename T>
void func( typename T::Integer t ) {
std::cout << "Test class" << std::endl;
}
template <typename T>
void func( T t ) {
std::cout << "not Test class" << std::endl;
}
// 引数の型は Test::Integer になるので、前者の func が呼ばれる.
func<Test>( 1 );
// 前者の func では引数の型が int::Integer となり成立しないので、後者の func が呼ばれる.
func<int>( 2 );
その他様々なユーティリティ
戻り値の型を後置する関数宣言構文
戻り値を関数の宣言の先頭ではなく末尾に配置することができるようになりました。
その場合、関数宣言の先頭は auto
にする必要があります。
// 従来.
int func() {
...
}
// C++11以降
auto func() -> int {
...
}
コンパイル時アサート
assert
は実行時に判断されますが、コンパイル時に判断される static_assert
が追加されました。
条件を満たさないとコンパイルエラーになります。
static_assert( ( sizeof(int) == 4 ), "int must be 4 bytes" );
生文字列リテラル
文字列の中に改行やダブルクォーテーションが多く含まれると、エスケープ文字だらけで読みにくくなっていました。
const char* json = "{\"name\": \"Taro\",\n\"age\": 25}";
C++11では、生文字列を R"(
と )"
で囲むと、その文字列のエスケープシーケンスが無視されるようになります。
const char* json = R"({"name": "Taro",
"age": 25})";
上記の例は、どちらも以下の文字列になります。
{"name": "Taro",
"age": 25}
char16_tとchar32_t
UTF-16 用の char16_t
と UTF-32 用の char32_t
という文字型が追加されました。
文字列リテラルに u
というプレフィックスを付けると UTF-16 の文字列に、 U
というプレフィックスを付けると UTF-32 の文字列になります。
char16_t s1[] = u"じゅうろくびっと";
char32_t s2[] = U"さんじゅうにびっと";
UTF-8文字列リテラル
文字列リテラルに u8
というプレフィックスを付けると、UTF-8 の文字列にエンコードされます。
char c[] = u8"はちびっと";
属性構文
二重の角カッコで囲むことで属性構文を指定できるようになりました。
属性構文とは、追加の情報をコンパイラに伝えるための構文です。
C++11 では以下の2つしかありません。
- [[noreturn]]
- 関数が返らないことを意味します。
- 「返らない」= 必ず例外発生 OR 必ず
std::abort()
OR 必ずstd::exit()
- [[carries_dependency]]
- 難しいので省略
[[noreturn]] void throwError() {
throw std::runtime_error( "error" );
}
C++14 以降で仲間が増えますが、C++11 時点では使い道なさそうです。