この記事は Competitive Programming Advent Calendar Div2013 の 12 月 23 日の記事として書かれました.
C++11 のすすめ
C++03 は普段から使っているけども C++11 はまだ触ったことがない人向けの入門記事です.最近は多くのプログラミングコンテストで C++11 が使えるようになりつつあります.C++11 で大幅に C++
が書きやすくなっているので,ぜひこの記事を読んで C++11 を使い始めてください!この記事では C++11 に追加された多数の新機能のうちコンテストに役立ちそう(もしくは多くの人が使いそうな)機能を紹介します.
手元での実行の仕方
GCC もしくは clang を利用していれば -std=c++0x というオプションを追加すれば C++11 の機能が有効になります.
$ g++ -std=c++0x -o program program.cc
テンプレートの右シフト問題
// [C++03] では >> が右シフトとして解釈されコンパイルエラーがでます.
// [C++11] では vector<vector<int>> と正しく認識できます.
vector<vector<int>> v;
nullptr
C++03 では NULL が int 型の 0 として定義されていました.これはオーバーロードでポインタではなく数値として優先的に使われるため意図しない動作をすることがありました.そこで C++11 では nullptr_t 型の
nullptr という値が用意されました.これ以降は nullptr を用いることが推奨されています.
// [C++03] ではマクロで定義された int 型の NULL を用いていました.
void* x = NULL;
// [C++11] では nullptr_t 型の nullptr が用意されました.
void* x = nullptr;
initializer_list
initializer_list が導入され vector や set 等の初期化が容易になりました.
vector<int> v = {1, 2, 3, 4, 5};
set<int> s = {1, 2, 3, 4, 5};
vector<vector<int>> v = {{1, 2, 3}, {4, 5, 6, 7}};
map<string, string> m = {{"hoge", "Hoge"}, {"piyo", "Piyo"}};
auto と decltype
GCC では __typeof を利用することにより型推論をすることができましたが,C++11 では標準で型推論が可能となりました.__typeof も decltype という別の名称ですが,標準で使えるようになりました.
// [C++03] 従来の方法.
set::iterator x = a.begin();
// [GCC C++03] __typeof を用いた方法.
__typeof(a.begin()) x = a.begin();
// [C++11] decltype を用いた方法.
decltype(a.begin()) x = a.begin();
// [C++11] auto を用いた方法.
auto x = a.begin();
auto の修飾
auto に対して const や参照等の制限をかけることもできます.
string s = "hoge";
// 参照.
auto& x1 = s;
// 書き換え不可な参照.
const auto& x2 = s;
関数の返り値の型推論
C++11 では return 値から直接型推論することはできませんが,decltype を用いて関数の返り値の型を決定させることができます.
decltype(cos(0)) Function(double r) {
return cos(r);
}
しかし,これは以下のようには書き換えられません.
// decltype 中の r は未定義なのでエラー.
decltype(cos(r)) Function(double r) {
return cos(r);
}
これに対して,C++11 では以下の様な解決法が与えられています.
auto Function(double r) -> decltype(cos(r)) {
return cos(r);
}
range-based for
今まで多くの競技プログラマーは FOREACH などのマクロを用意し,vector や set の要素を操作していましたが,C++11 では range-based for という機能が追加され非常にシンプルに書けるようになりました.
// [C++03] での標準的な方法.
set<int> a;
for (set<int>::iterator iter = a.begin(); iter != a.end(); iter++) {
std::cout << *iter << std::endl;
}
// [GCC C++03] で __typeof を使い FOREACH マクロを定義する方法.
#define FOREACH(iter, a) \
for (__typeof((a).begin()) iter = (a).begin(); iter != (a).end(); iter++)
set<int> a;
FOREACH(iter, a) {
std::cout << *iter << std::endl;
}
// [C++11] で range-based for を使った方法.
set<int> a;
for (int i : a) {
std::cout << i << std::endl;
}
// [C++11] で auto および range-based for を使った方法.
set<int> a;
for (auto i : a) {
std::cout << i << std::endl;
}
unique_ptr
new したオブジェクトは忘れず delete をしなければメモリリークの原因になりますが,C++03 まで標準ではこれに対する解決策は提示されていませんでした.例えば,以下のようなコードのように,関数の中で return をしたり
for 文中で break をした時に new したオブジェクトを delete し忘れるようなことはよくありました.
void FunctionA() {
ClassA* object_a = new ClassA;
object_a->MethodA();
if (...) {
object_a->MethodB();
// ここで return してしまうと,ここを通るパスでは delete ができずメモリリークが起きます.
return;
}
delete object_a;
}
これを C++11 の unique_ptr を用いて置き換えると以下のようなコードになり,うっかりメモリリークを起こしてしまうことはほとんどなくなります.
#include <memory>
void FunctionA() {
std::unique_ptr<ClassA> object_a(new ClassA);
object_a->MethodA();
if (...) {
object_a->MethodB();
// ここで return しても自動的に内部的に delete されます.
return;
}
// unique_ptr を使うと明示的に delete をする必要はなくなります.
}
unique_ptr の利点
unique_ptr は余分にメモリを消費することはありません.その変数が含まれるスコープを出る時に,内部が nullptr でなければ delete をするだけの仕様です.経験的にほとんどのポインターを必要とするコードは
unique_ptr で置き換えることができます.unique_ptr
で書かれたコードは,参照カウンタ用の領域が不必要なだけではなく,必要な時に必要なメモリを確保し不必要になった時点ですぐに解放するコードになるので,非常にメモリ効率の良いコードになります.またマーク・アンド・スイープのような余分な処理も実行する必要がなく,計算リソースの面でも非常に効率が良いコードになります.
通常のポインターとの対応
変数の宣言
変数の宣言は以下のように,コンストラクタの引数にポインターを与える形で行います.もし最初にポインターで初期化する必要がなければ,そのまま宣言することも可能です.
// 与えるポインターがある場合.
ClassA* object_a = new ClassA;
std::unique_ptr<ClassB> object_b(new ClassB);
// 与えるポインターがない場合.
ClassA* object_a = nullptr;
std::unique_ptr<ClassB> object_b;
メソッドの呼び出し
unique_ptr は -> を用いて通常のポインターのようにアクセスができます.ただし,object_b の中身があるかどうか(nullptr であるかどうか)の判定は行わないので,それは今まで通りユーザに責任があります.
object_a->Method();
object_b->Method();
間接参照(ポインターの実体化)
間接演算子もメソッドの呼び出しと同様に,通常のポインターのようにアクセスできます.
(*object_a).Method();
(*object_b).Method();
ポインターの取得
通常のポインターはそれ自身がポインターですが,unique_ptr を用いた場合は get() メソッドを用いてポインターを取得します.get() は所有権の移譲は行われないので,そのポインターの中身の寿命は引き続き
unique_ptr オブジェクトが管理することになります.
if (object_a == nullptr) return;
if (object_b.get() == nullptr) return;
所有権の解放
release() メソッドは所有権を解放し,内部のポインターを nullptr にします.
return object_a;
retrun object_b.release();
ポインターの上書き
reset() メソッドは必要に応じて内部のポインターを delete し,新しいポインターで置き換えます.
if (object_a) delete object_a; object_a = new ClassA;
object_b.reset(new ClassB);
shared_ptr
unique_ptr はポインターを複数の場所で管理することはできません.もしそのようなことが必要になった時には,shared_ptr という別のスマートポインターを使うことができます.shared_ptr
は内部に参照カウンタを持ち,スマートポインターのコピーが可能になります.ただし shared_ptr が本当に必要になる場面は非常に少ないはずですので,多用しないように心がけましょう.
array
C++11 では固定長配列を vector 等と同様に扱えるようにするため,array が追加されました.begin() や end(),size() などが用意されており,template
を用いて汎化された関数やメソッドに対して固定長配列が与えられるようになりました.
array<int, 5> a = {1, 2, 3, 4, 5};
template<typename Container> Reverse(Container* container) {
reverse(container->begin(), container->end());
}
Reverse(a);
emplace, emplace_back
C++11 では vector 等に emplace_back 等のメソッドが追加されました.見た目は非常に push_back や insert
等と似通っていますが,実オブジェクトではなく引数を与えるという点で違います.引数を直接渡すためコピーが発生しないという特徴があります.
vector<int> a = {1, 2, 3};
vector<vector<int>> b;
// b.push_back(vector<int>(a.begin(), a.end())); とほぼ同義
b.emplace_back(a.begin(), a.end());
右辺値参照 (rvalue reference), move
C++11 では型に右辺値参照という概念が追加されました.右辺値参照の変数は中身が投げ捨てられているのでコピーする必要がないということを明示的に表しています.
vector<vector<int>> a;
for (int i = 0; i < 1000; i++) {
vector<int> b;
for (int j = i; 0 < j; j--) {
b.push_back(j);
}
// 最適化がうまくかからなければここで b のコピーが発生する
a.push_back(b);
}
vector<vector<int>> a;
for (int i = 0; i < 1000; i++) {
vector<int> b;
for (int j = i; 0 < j; j--) {
b.push_back(j);
}
// b のコピーは発生しない(再利用される)
a.push_back(std::move(b));
// ただし,これ以降に b の中身は使ってはならない.
}
有用なリンク