2016年08月19日更新 (訳注: 和訳は2021年09月19日更新)
この文書 (訳注: 原文) は Bjarne Stroustrup が記述、更新している。 建設的なコメント、訂正、参考資料、提案はもちろん大いに歓迎する。 現在、著者は参考資料の充実化と清書に取り組んでいる。
翻訳:
著者は、自身が理事を務める C++ 財団が保守する、新たな、統合された、isocpp.org C++ FAQ に寄贈した。 この FAQ の保守はどんどん頻繁にされるようになるだろう。
C++11 は 2011 年に承認された ISO C++ 標準である。 以前の標準は、しばしば C++98 や C++03 と呼ばれる; C++98 と C++03 の違いはごく僅かで技術的なものなので、ユーザの興味はひかないだろう。
最近のワーキングペーパー が閲覧可能だ。 これは 2011 年 8 月の国際投票において 21-0 で正式に承認された国際標準最終案とほとんど同じだ。 正式に標準が承認されるまで、著者達は来たるべき標準を C++0x と呼んでいた。 名前の整合性をとるように更新する時間が著者にはなかったのだが、まあなんだ、C++0x という名前が気に入っているわけだ。 :-) "C++0x"という名前は、著者やその他の人々が、C++08 または C++09 になると思っていた時の名残である。 「x」は十六進数だと考えて欲しい (つまり C++0B == C++11)。
C++11/C++0x に関連する公式の文書は ISO C++ 委員会のウェブサイトにある。 委員会の公式な名称は SC22 WG21 だ。
注意: この FAQ は長きに亘って更新中であり続けるだろう。 意見、質問、参考資料、訂正、提案を歓迎する。
以下はいくらか高水準な質問である
質問 個々の言語機能についての質問は以下の通り:
質問 個々の標準ライブラリ機能についての質問は以下の通り:
以降は上述した個々の質問に対する答えである。
著者の理想は、システム設計と実装について異なる考えを持つプログラマを支援するためのプログラミング言語機能として使われることだ。 著者は、C++11 にそれが可能だと考えている - そして、C++ プログラマのみならず、システムプログラミングの一般的かつ非常に幅広い分野で様々な現代的プログラミング言語に慣れているプログラマに対しても、可能だと考えている。
言い換えれば、著者はまだ楽観主義者である。
新しい標準がどのような姿になるかはもう分かっている (提供される個々の機能の小さい変更を除く)。 この新しい標準は C++11 と呼ばれるようになるだろうが、ちょっとしたお役所仕事の遅れのせいで C++12 になるかも知れない。 個人的には、何もつけずに C++ と呼び、以前の C++ と区別して年号をつける必要がある時に限り、ARM C++、C++98、C++03 と呼ぶのが好みである。 移行期間の間、著者はあちこちで C++0x という用語を使うだろう。 「x」は十六進数だと考えていただきたい。
著者は、2012 年内のどこかで最初の完全な C++11 コンパイラが現れるのではないかと思うが、いつそういったコンパイラが出荷されるかとか、いつ全てのコンパイラが C++11 の機能を全部提供するのがいつになるかとかは気にしていない。 全ての C++11 機能はどこかで誰かが実装してきたものなので、実装者はその経験を参考にすることができるということは明記しておく。
以下に提供元からの C++11 情報へのリンクを挙げる:
提案の洪水の中から合理的に選択するために、著者達は明確な設計目標のあり方を工夫した。 著者達は完全にそれに従ったが、全ての細部にわたって委員会を導くほどには完全ではなかった (そして、著者の意見を言わせていただくと、完全たり得ない)。
その結果、プログラミング言語の抽象性が大きく進歩した。 C++ がエレガントでフレキシブルに、かつ手書きで調製したコードに比べてゼロコストで表現できる範囲が大幅に広がったのだ。 著者達が「抽象化」と言うと、一般的には「クラス」や「オブジェクト」だと考えられてしまう。 C++11 は遙かに先を行っている: 初期化リスト、 統一された初期化、 template の別名、 右辺値リファレンス、 default 及び delete 関数、 可変個引数 template のような機能の追加によって、明確かつ安全に記述できるユーザ定義型の範囲が広がった。 その実装は auto、 継承コンストラクタ、 decltype のような機能の追加で簡単になっている。 これらの改良は C++11 が新しい言語のように感じさせるのに十分だ。
受け入れられた言語機能の一覧については、機能の一覧を参照されたい。
受け入れられたライブラリの一覧については、ライブラリコンポーネントの一覧を参照されたい。
より詳しくは、以下を参照されたい:
利用分野や利用方法に着目することでも、細かい目標を見て行くことができる:
ある:
多くの国に活発な C++ グループを抱える国内標準化団体がある。 これらのグループは会合を開いたりウェブで調整したりして、ISO の会合に代表を送ってくる。 カナダ、フランス、ドイツ、スイス、イギリス、アメリカは殆どの会合に出席する。 デンマーク、オランダ、日本、ノルウェー、スペイン、その他の国々は個人で出席するが、前者ほど頻繁ではない。
作業の殆どは会合と会合の間にウェブ越しに進行し、その結果が大量の委員会文書として WG21 のウェブサイトに掲載されている。
委員会の会合は年に二〜三回、一週間の間、開催される。 殆どの作業は、「コア」「ライブラリ」「発展」「並列性」等の分科会に分かれて行われる。 必要な場合は、「concept」や「メモリモデル」といった特定の緊急事案について、会合と会合の間に臨時ワーキンググループの会合が開催されることもある。 投票は主会合で行われる。 まず、その課題が委員会全体へ提示する準備ができているか、ワーキンググループで非公式な投票が実施される。 続いて、委員会の全体投票 (一人一票) が行われ、承認されれば国際投票となる。 著者達は多数の出席者と国々が合意しない状況に陥らないように大変気を使っている -- そうなれば長期間の論争になるのが分かり切っているからだ。 公式案の最終投票は国別の標準化団体毎に郵送で行われる。
委員会は C の標準化グループ (SC22 WG14) や POSIX と公式に連携しており、その他のグループとも何らかの公式な連絡を取り合っている。
当然、多くの (全てではないが) ボランティア達が C++ に関わる日常の仕事に就いている: 著者達の中にはコンパイラ屋、ツール屋、ライブラリ屋、アプリケーション屋 (少なすぎる)、研究者 (少しは)、コンサルタント、テストスイート屋、等々がいる。
以下に挙げるのは参加している組織のごく一部だ: Adobe、 Apple、 Boost、 Bloomberg、 EDG、 Google、 HP、 IBM、 Intel、 Microsoft、 Red Hat、 Sun。
以下に挙げるのは文献やウェブで名前を見るかも知れない人達の短い一覧だ: Dave Abrahams、 Matt Austern、 Pete Becker、 Hans Boehm、 Steve Clamage、 Lawrence Crowl、 Beman Dawes、 Francis Glassborow、 Doug Gregor、 Pablo Halpern、 Howard Hinnant、 Jaakko Jarvi、 John Lakos、 Alisdair Meredith、 Jens Maurer、 Jason Merrill、 Sean Parent、 P.J. Plauger、 Tom Plum、 Gabriel Dos Reis、 Bjarne Stroustrup、 Herb Sutter、 David Vandevoorde、 Michael Wong。 ここに挙げることができなかった、200 人以上いる現在及び過去のメンバーの方々にはご容赦願いたい。 また、様々な文書の執筆者一覧にも目を向けて戴きたい: 標準は、匿名の委員ではなく、(多くの) 個人によって書かれているのだ。
あるいは、WG21 の文書の執筆者リストを調べて専門的技術の息吹と深みに感動するかもしれないが、たくさん書いたわけではないにせよ、標準化作業に貢献した人も大勢いることを忘れないで欲しい。
徹底的に単純化された『concept lite』が (テクニカルレポートとして) C++14 の一部になるだろう。
この文書の「concept」の項は削除せずに残してある:
auto x = 7;ここで x は、初期化子の型の通り int 型となる。 一般に、
auto x = 式;と書くことができ、x の型は「式」の計算結果の型になる。
変数の型を初期化子から導出する auto 型は、明らかに、型を正確に知るのや書くのが難しい場合に一番便利だろう。 以下を考えよ:
template<class T> void printall(const vector<T>& v) { for (auto p = v.begin(); p!=v.end(); ++p) cout << *p << "\n"; }C++98 では、
template<class T> void printall(const vector<T>& v) { for (typename vector<T>::const_iterator p = v.begin(); p!=v.end(); ++p) cout << *p << "\n"; }と書く必要があった。
変数の型が template 引数に強く依存している場合は、auto なしにコードを書くのは本当に難しくなるだろう。 例えば:
template<class T, class U> void multiply(const vector<T>& vt, const vector<U>& vu) { // ... auto tmp = vt[i]*vu[i]; // ... }tmp の型は T と U の乗算結果になるが、それが正確に何型であるか人間の読み手が見出すのは難しい。 しかし、もちろんコンパイラは、自身が扱っている適切な T と U が何型であるかを知っている。
auto 機能はもっとも早く提案され実装された部分だ: 著者は 1984 年の早い時期に自分の Cfront で動作させていたが、C との互換性問題のせいで外したのだ。 この互換性問題は、C++98 と C99 が「暗黙の int」の削除を受け入れた時に解決した; 両方の言語で、全ての変数と関数が、明示的に型定義が必要になったのだ。 auto の古い意味 (「これはローカル変数だよ」) はもはや非合法となった。 複数の委員会メンバーが何百万行ものコードを洗ったが、数えるほどの使用例しか見つからなかった -- しかもほとんどはテストスイートか、あるいはバグのようだった。
auto は主にコードの表記を簡潔にするための機能なので、標準ライブラリの仕様には影響しない。
以下も参照。
void f(vector<double>& v) { for (auto x : v) cout << x << '\n'; for (auto& x : v) ++x; // 値を更新できるようにリファレンスを使う }これを「v 中の全ての x について (原文: for all x in v)」、v.begin() から始めて v.end() まで反復する、と読むこともできる。 もう一つの例:
for (const auto x : { 1,2,3,5,8,13,21,34 }) cout << x << '\n';begin() (と end()) は x.begin() として呼び出されるメンバ関数でも良いし、begin(x) として呼び出される独立した関数でも良い。 メンバ関数版の方が優先順位が高い。
以下も参照。
list<vector<string>> lvs;C++98 では、二つの > の間にスペースがないので、文法エラーになる。 C++11 では、この二つ > を正しく二つの template 引数リストの終わりだと認識する。
なぜこれが今まで問題だったのか? コンパイラフロントエンドはパース/ステージに構成されている。 もっとも簡単なモデルは以下のようになる:
以下も参照。
class X { // ... X& operator=(const X&) = delete; // コピーの禁止 X(const X&) = delete; };逆に、既定のコピーの振る舞いが望ましいなら、それを明示することもできる:
class Y { // ... Y& operator=(const Y&) = default; // デフォルトコピーの意味 Y(const Y&) = default; };既定の動作を明示するのは冗長だ。 とは言え、コピー動作についてのコメントとして効果はあるし、(さらに悪いことに) 既定の振る舞いを意味するコピー動作を明示的に定義するユーザが珍しくない。 既定の振る舞いのままにしておくようコンパイラに指示する方法が単純なら、エラーになりにくいし、より良いオブジェクトコードを生成することもあるだろう。
「default」機構は既定動作を持つ全ての関数で使用できる。 「delete」機構はどんな関数でも使用できる。 例えば、このような望ましくない変換を消去することができる:
struct Z { // ... Z(long long); // long long で初期化できる Z(long) = delete; // それより短い初期化は不可 };
以下も参照。
移動、コピー、デストラクタのどれも、ユーザにより明示的に指定される (宣言、定義、=default、=delete) と、移動がデフォルト生成されなくなる。 移動、コピー、デストラクタのどれも、ユーザにより明示的に指定される (宣言、定義、=default、=delete) と、未宣言のコピー操作がデフォルト生成されるが、これは廃止予定であるため、頼ってはならない。 例えば:
class X1 { X1& operator=(const X1&) = delete; // コピーの禁止 };これは暗黙に X1 の移動も禁止する。 コピー初期化は許されるが、廃止予定だ。
class X2 { X2& operator=(const X2&) = delete; };これは暗黙に X2 の移動も禁止する。 コピー初期化は許されるが、廃止予定だ。
class X3 { X3& operator=(X3&&) = delete; // 移動の禁止 };これは暗黙に X3 のコピーも禁止する。
class X4 { ~X4() = delete; // デストラクトの禁止 };これは暗黙に X4 の移動も禁止する。 コピーは許されるが、廃止予定だ。
著者は、これら五つの関数のうち一つを宣言するなら、全部を明示的に宣言することを強く推奨する。 例えば:
template<class T> class Handle { T* p; public: Handle(T* pp) : p{pp} {} ~Handle() { delete p; } // ユーザ定義のデストラクタ: 暗黙のコピーや移動なし Handle(Handle&& h) :p{h.p} { h.p=nullptr; } // 所有権を移転 Handle& operator=(Handle&& h) { delete p; p=h.p; h.p=nullptr; return *this; } // 所有権を移転 Handle(const Handle&) = delete; // コピーなし Handle& operator=(const Handle&) = delete; // ... };
以下も参照。
enum class (「強い enum」) は強く型付けされスコープを持つ:
enum Alert { green, yellow, orange, red }; // 従来の enum enum class Color { red, blue }; // スコープ付きの強く型付けされた enum // 列挙体の名前はスコープ内に閉じこめられる // 暗黙的に int に変換されない enum class TrafficLight { red, yellow, green }; Alert a = 7; // エラー (これまでの C++ と同じ) Color c = 7; // エラー: int → Color の変換は不可 int a2 = red; // ok: Alert → int の変換 int a3 = Alert::red; // C++98 ではエラー; C++11 では ok int a4 = blue; // エラー: blue はスコープ外 int a5 = Color::blue; // エラー: Color → int の変換 Color a6 = Color::blue; // okこのように、従来の enum はそのまま動作するが、enum の名前で修飾することもできるようになった。
新しい enum は、従来の列挙体 (名前付の値) と class (スコープ付きのメンバと無変換性) の側面を組み合わせたものなので、「enum class」と呼ばれる。
下敷きとなる型を指定できるようになったことで、列挙体の相互運用性と大きさの保証が単純になった:
enum class Color : char { red, blue }; // コンパクトな表現 enum class TrafficLight { red, yellow, green }; // 既定では、下敷きとなる型は int enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U }; // E はどのくらい大きい? // (古い規約が何と言おうが // これは「実装定義」) enum EE : unsigned long { EE1 = 1, EE2 = 2, EEbig = 0xFFFFFFF0U }; // これからは型を指定できるenum の前方宣言も可能になった:
enum class Color_code : char; // (前方) 宣言 void foobar(Color_code* p); // 前方宣言の利用 // ... enum class Color_code : char { red, yellow, green, blue }; // 定義下敷きとなる型は、符号ありまたは符号なしの整数型でなくてはならない; 既定の型は int だ。
標準ライブラリでは、enum classe は
以下も参照。
enum Flags { good=0, fail=1, bad=2, eof=4 }; constexpr int operator|(Flags f1, Flags f2) { return Flags(int(f1)|int(f2)); } void f(Flags x) { switch (x) { case bad: /* ... */ break; case eof: /* ... */ break; case bad|eof: /* ... */ break; default: /* ... */ break; } }ここで、constexpr は、この関数が単純な形式でなければならず、従ってコンパイル時に定数式として評価できることを表す。
コンパイル時に式が評価できることに加えて、コンパイル時に評価する必要があることを表したい場合、変数定義の前に constexpr (暗黙に const となる) を置くことで以下のように書ける:
constexpr int x1 = bad|eof; // ok void f(Flags f3) { constexpr int x2 = bad|f3; // エラー: コンパイル時に評価できない int x3 = bad|f3; // ok }典型的な場合、大域オブジェクトや namespace オブジェクト、場合によっては読込専用記憶域に格納したいオブジェクトに対して、コンパイル時評価を保証したいことが多いだろう。
これは、コンストラクタを constexpr にできるくらい単純なオブジェクトや、そのようなオブジェクトを含む式に対しても機能する:
struct Point { int x,y; constexpr Point(int xx, int yy) : x(xx), y(yy) { } }; constexpr Point origo(0,0); constexpr int z = origo.x; constexpr Point a[] = {Point(0,0), Point(1,1), Point(2,2) }; constexpr int x = a[1].x; // x は 1 になるconstexpr は const を一般的に置き換えるものではない (し、その逆も然りである) ことに注意:
以下も参照。
void f(const vector<int>& a, vector<float>& b) { typedef decltype(a[0]*b[0]) Tmp; for (int i=0; i<b.size(); ++i) { Tmp* p = new Tmp(a[i]*b[i]); // ... } // ... }この考えは、総称型プログラミング業界では長い間「typeof」という名前で呼ばれることが多かったが、実際の typeof の実装は不完全で、しかも互換性がなかったため、標準の名前は「decltype」になった。
単に、変数を初期化するための型が欲しいだけなら、auto がよりシンプルな選択肢であることが多いだろう。 本当に decltype が必要になるのは、変数以外、例えば戻り値型のような型が欲しい場合だ。
以下も参照。
vector<double> v = { 1, 2, 3.456, 99.99 }; list<pair<string,string>> languages = { {"Nygaard","Simula"}, {"Richards","BCPL"}, {"Ritchie","C"} }; map<vector<string>,vector<int>> years = { { {"Maurice","Vincent", "Wilkes"},{1913, 1945, 1951, 1967, 2000} }, { {"Martin", "Ritchards"}, {1982, 2003, 2007} }, { {"David", "John", "Wheeler"}, {1927, 1947, 1951, 2004} } };初期化リストは、もはや配列だけのものではない。 {} リストを受取るための仕組みは、std::initializer_list<T> 型の引数を受取る関数 (しばしばコンストラクタ) による。 例えば:
void f(initializer_list<int>); f({1,2}); f({23,345,4567,56789}); f({}); // 空のリスト f{1,2}; // エラー: 関数呼出 ( ) がない years.insert({{"Bjarne","Stroustrup"},{1950, 1975, 1985}});初期化リストの長さは可変だが、型は一様でなくてはならない (全ての要素が template 引数型 T、もしくは T へ変換可能な型でなくてはならない)。
コンテナは初期化リストコンストラクタを以下のように実装してもよい:
template<class E> class vector { public: vector (std::initializer_list<E> s) // 初期化リストコンストラクタ { reserve(s.size()); // 正確な領域長を取得 uninitialized_copy(s.begin(), s.end(), elem); // 要素の初期化 (elem[0:s.size()) の範囲) sz = s.size(); // vector の大きさを設定 } // ... 上述の通り ... };
直接初期化とコピー初期化の差異は、{} 初期化でもそのままだが、{} 初期化である以上、あまり相関関係はなくなる。 例えば、std::vector は、int からの explicit コンストラクタと初期化リストコンストラクタを持っている:
vector<double> v1(7); // ok: v1 は 7 つの要素を持つ v1 = 9; // エラー: int から vector<double> への変換は不可 vector<double> v2 = 9; // エラー: int から vector<double> への変換は不可 void f(const vector<double>&); f(9); // エラー: int から vector<double> への変換は不可 vector<double> v1{7}; // ok: v1 は 1 つの要素を持つ (値は 7.0) v1 = {9}; // ok: v1 は 1 つの要素を持つようになった (値は 9.0) vector<double> v2 = {9}; // ok: v2 は 1 つの要素を持つ (値は 9.0) f({9}); // ok: f がリスト { 9 } で呼び出される vector<vector<double>> vs = { vector<double>(10), // ok: explicit コンストラクタ (10 要素) vector<double>{10}, // ok: explicit コンストラクタ (1 要素、値は 10.0) 10 // エラー: vector のコンストラクタは explicit };
関数は、initializer_list に対して、不変なシーケンスとしてアクセスすることができる。 例えば:
void f(initializer_list<int> args) { for (auto p=args.begin(); p!=args.end(); ++p) cout << *p << "\n"; }
単独の std::initializer_list 型の引数を取るコンストラクタを初期化リストコンストラクタと呼ぶ。
標準ライブラリコンテナ、string、regex は初期化リストコンストラクタや代入演算子等を持っている。 初期化リストを、例えば範囲 for 文で、範囲として使うこともできる。
初期化リストは統一および一般化された初期化の枠組みの一環だ。
以下も参照。
int x = 7.3; // おっと! void f(int); f(7.3); // おっと!しかしながら、C++11 の {} 初期化は縮小変換を行わない:
int x0 {7.3}; // エラー: 縮小変換 int x1 = {7.3}; // エラー: 縮小変換 double d = 7; int x2{d}; // エラー: 縮小変換 (double から int) char x3{7}; // ok: 7 は int だが、これは縮小変換ではない vector<int> vi = { 1, 2.3, 4, 5.6 }; // エラー: double から int への縮小変換C++11 での大量の非互換を避けるため、何が縮小変換にあたるかを決定する際に、初期化子の型ではなく実際の値 (上記の例では 7) で判断する。 もし、元の値が代入先の値として正確に表現できるのなら、それは縮小変換ではない。
char c1{7}; // OK: 7 は int だが、char に収まる char c2{77777}; // エラー: 縮小変換 (8-bit char と仮定)浮動小数から整数への変換は、常に縮小変換と看做されることに注意すること -- たとえ、7.0 から 7 であってもだ。
以下も参照。
class X { int a; void validate(int x) { if (0<x && x<=max) a=x; else throw bad_X(x); } public: X(int x) { validate(x); } X() { validate(42); } X(string s) { int x = lexical_cast<int>(s); validate(x); } // ... };
これは明らかに可読性の邪魔になるし、繰り返し書くのはエラーになりやすい。 どちらもメンテナンスには邪魔だ。 そこで、C++11 では、一つのコンストラクタを別のコンストラクタのために定義することができるようになった:
class X { int a; public: X(int x) { if (0<x && x<=max) a=x; else throw bad_X(x); } X() :X{42} { } X(string s) :X{lexical_cast<int>(s)} { } // ... };
以下も参照
int var = 7; class X { static const int m1 = 7; // ok const int m2 = 7; // エラー: static でない static int m3 = 7; // エラー: const でない static const int m4 = var; // エラー: 定数式でない初期化 static const string m5 = "odd"; // エラー: 整数型でない // ... };C++11 の基本的な考えは、static でないデータメンバが (その class 内で) 宣言されたところで初期化されることを許容する、というものだ。 従って、コンストラクタは、実行時に初期化が必要となった時に初期化子を呼び出すことができる。 以下を考えよ:
class A { public: int a = 7; };これは以下と等価だ:
class A { public: int a; A() : a(7) {} };これでタイピングをちょっとだけ削減できるが、本当の利点は class 内に複数のコンストラクタが存在する場合に現れる。 全てのコンストラクタが、あるメンバを同じように初期化するのはよくあることだ:
class A { public: A(): a(7), b(5), hash_algorithm("MD5"), s("Constructor run") {} A(int a_val) : a(a_val), b(5), hash_algorithm("MD5"), s("Constructor run") {} A(D d) : a(7), b(g(d)), hash_algorithm("MD5"), s("Constructor run") {} int a, b; private: HashingFunction hash_algorithm; // 全ての A のインスタンスに対して適用する暗号ハッシュ std::string s; // オブジェクトのライフサイクルを示す状態文字列 };hash_algorithm と s にはそれぞれ唯一の既定値があるという事実が大量のコードの中に埋もれてしまい、メンテナンスで容易に問題になり得る。 代わりに、データメンバの初期化を取り除くことができる:
class A { public: A(): a(7), b(5) {} A(int a_val) : a(a_val), b(5) {} A(D d) : a(7), b(g(d)) {} int a, b; private: HashingFunction hash_algorithm{"MD5"}; // 全ての A のインスタンスに対して適用する暗号ハッシュ std::string s{"Constructor run"}; // オブジェクトのライフサイクルを示す状態文字列 };メンバが class 内初期化子とコンストラクタの両方で初期化される場合、コンストラクタの方だけで初期化される (既定を「オーバーライド」する)。 従って、より単純化することができる:
class A { public: A() {} A(int a_val) : a(a_val) {} A(D d) : b(g(d)) {} int a = 7; int b = 5; private: HashingFunction hash_algorithm{"MD5"}; // 全ての A のインスタンスに対して適用する暗号ハッシュ std::string s{"Constructor run"}; // オブジェクトのライフサイクルを示す状態文字列 };
以下も参照。
struct B { void f(double); }; struct D : B { void f(int); }; B b; b.f(4.5); // 大丈夫 D d; d.f(4.5); // びっくり: 引数 4 で f(int) を呼び出すC++98 では、基底 class から派生 class へオーバーロードされる関数を「取込む」ことができた:
struct B { void f(double); }; struct D : B { using B::f; // B からこのスコープへ全ての f() を取込む void f(int); // 新たな f() を追加 }; B b; b.f(4.5); // 大丈夫 D d; d.f(4.5); // 大丈夫: B::f(double) である D::f(double) を呼び出す著者はかつて「ほとんど歴史上の事故で、コンストラクタではこの using の用法が通常のメンバ関数と同様には機能しない」と言ったことがある。 C++11 は以下の支援機能を提供する:
class Derived : public Base { public: using Base::f; // Base の f を Derived のスコープへ取込む -- C++98 でも動く void f(char); // 新たな f を提供 void f(int); // この f は Base::f(int) よりも優先 using Base::Base; // Base のコンストラクタを Derived のスコープへ取込む -- C++11 のみ Derived(char); // 新たなコンストラクタの提供 Derived(int); // このコンストラクタは Base::Base(int) よりも優先 // ... };
そうした場合、初期化が必要な新しいメンバ変数を定義している派生 class の継承コンストラクタで、自分の足を撃ってしまうことがある:
struct B1 { B1(int) { } }; struct D1 : B1 { using B1::B1; // 暗黙的な D1(int) の宣言 int x; }; void test() { D1 d(6); // おっと: d.x が初期化されない D1 e; // エラー: D1 に既定のコンストラクタがない }メンバ初期化子を使って自分の足を撃たないようにできる:
struct D1 : B1 { using B1::B1; // 暗黙的な D1(int) の宣言 int x{0}; // メモ: x は初期化される }; void test() { D1 d(6); // d.x はゼロ }
以下も参照。
static_assert(式,文字列);コンパイラは式を評価し、その結果が偽 (つまりアサーションが失敗) の場合、文字列をエラーメッセージとして出力する。 例えば:
static_assert(sizeof(long)>=8, "64-bit コードの生成がこのライブラリには必要"); struct S { X m1; Y m2; }; static_assert(sizeof(S)==sizeof(X)+sizeof(Y),"予期しないパディングが S 中に存在");static_assert はプログラムとその処理について、コンパイラによる明示的な前提条件を置く際に役に立つだろう。 static_assert はコンパイル時に評価するため、実行時の値に依存する前提条件をチェックできないことに注意すること。 例えば:
int f(int* p, int n) { static_assert(p==0,"p is not null"); // エラー: static_assert() 式が定数式でない // ... }(代わりに、テストして失敗した場合に例外を投げるようにすること)
以下も参照。
long long x = 9223372036854775807LL;いや、long long long 型はないし、long 型を short long long と綴ることもできない。
以下も参照。
char* p = nullptr; int* q = nullptr; char* p2 = 0; // 0 も従来の通り使用可能で、p==p2 void f(int); void f(char*); f(0); // f(int) を呼び出す f(nullptr); // f(char*) を呼び出す void g(int); g(nullptr); // エラー: nullptr は int ではない int i = nullptr; // エラー: nullptr は int ではない
以下も参照。
template<class T, class U> ??? mul(T x, U y) { return x*y; }この戻り値型を何と書けば良いだろう? もちろん答えは「x*y の型」なのだが、それをどうやって書けば良いかということだ。 最初の考えは、「decltype」を使うことだ:
template<class T, class U> decltype(x*y) mul(T x, U y) // スコープ問題! { return x*y; }x と y がスコープ中にないため、これはうまくいかない。 しかしながら、こう書くことならできる:
template<class T, class U> decltype(*(T*)(0)**(U*)(0)) mul(T x, U y) // 醜い! しかも間違いやすい { return x*y; }おまけに、この「美しくない」呼び出し方は、過度に格式ばっている。
解は、戻り値型を引数の後に記述することだ:
template<class T, class U> auto mul(T x, U y) -> decltype(x*y) { return x*y; }ここで、「戻り値型を導出するか、後で指定する」ことを意味する auto 表記を用いた。
後置表記は template と型導出を主目的とするものではなく、スコープ問題を解決するためのものだ。
struct List { struct Link { /* ... */ }; Link* erase(Link* p); // p を削除し、p の直前のリンクを返却 // ... }; List::Link* List::erase(Link* p) { /* ... */ }最初の List:: が必須である理由は、List のスコープが二番目の List:: の後から始まるためだ。 より良い書き方:
auto List::erase(Link* p) -> Link* { /* ... */ }これで Link の明示的な修飾が不要になる。
以下も参照。
template<class T> using Vec = std::vector<T,My_alloc<T>>; // 自前アロケータを使った標準の vector Vec<int> fib = { 1, 2, 3, 5, 8, 13 }; // My_alloc を使った要素の割り当て vector<int,My_alloc<int>> verbose = fib; // verbose と fib は同じ型このキーワード using で「右辺の型を参照する名前」を記述するという線形な表記ができる。 著者達は、伝統的でひねくれた typedef で解決しようとしてはみたのだが、もっと不明瞭な文法に我慢しない限り、完全で首尾一貫した解決を得るには至らなかった。
特殊化も動作する (特殊化したものを別名に取ることはできるが、別名を特殊化することはできない)。 例えば:
template<int> struct int_exact_traits { // 考え: int_exact_trait<N>::type は正確に N bit の型 typedef int type; }; template<> struct int_exact_traits<8> { typedef char type; }; template<> struct int_exact_traits<16> { typedef char[2] type; }; // ... template<int N> using int_exact = typename int_exact_traits<N>::type; // 表記上の利便性のため、別名を定義 int_exact<8> a = 7; // int_exact<8> は 8 bit の整数template に関連する重要性に加えて、型の別名は異なる (そして著者に言わせればより良い) 文法として通常の型の別名にも使うことができる:
typedef void (*PFD)(double); // C スタイル using PF = void (*)(double); // using + C スタイルの型 using P = [](double)->void; // using + 後置戻り値型
以下も参照。
この例 (「可変個引数 template の簡単な紹介」(参考文献を参照) から引用) は、汎用的でタイプセーフな printf() の実装だ。 実際に使うなら boost::format の方が良いだろうが、まあ以下を考えよ:
const string pi = "円周率"; const char* m = "%sの値は大体 %g (%s 以外では)。\n"; printf(m, pi, 3.14159, "Indiana");一番簡単なパターンの printf() は、書式文字列だけの場合なので、最初はそれから扱おう:
void printf(const char* s) { while (s && *s) { if (*s=='%' && *++s!='%') // 他の引数を渡すことを意図していなかったか、念のため確認 // 書式文字列中の %% は % の意味 throw runtime_error("無効な書式: 引数がない"); std::cout << *s++; } }次に、引数が加わった printf() を扱わなければならない:
template<typename T, typename... Args> // "..." に注目 void printf(const char* s, T value, Args... args) // "..." に注目 { while (s && *s) { if (*s=='%' && *++s!='%') { // 書式指定子 (%% は無視) std::cout << value; // 最初の書式でない引数 return printf(++s, args...); // 最初の引数を「剥ぎ取る」 } std::cout << *s++; } throw std::runtime error("余分な引数が printf に渡された"); }このコードは単純に最初の書式でない引数を「剥ぎ取」って、自分自身を再帰的に呼び出して行き、 書式でない引数がなくなった時点で、最初の (単純な) printf (上述) を呼び出す。 これは、言わばコンパイル時に行われる、通常の関数型プログラミングだ。 << のオーバーロードが書式文字列中の (間違っているかも知れない)「ヒント」に取って代わっていることに注意されたい。
Args... は、いわゆる『引数パック』を定義している。 これは基本的に (型/値) ペアの順列で、その先頭から引数を『剥ぎ取る』ことができる。 printf() が引数一つで呼び出された場合は、最初の定義 (printf(const char*)) が選択される。 printf() が二つ以上の引数で呼び出された場合は、二つ目の定義 (printf(const char*, T value, Args... args)) が選択され、その第一引数は s、第二引数は value、そして残り全部は (それが何だろうと) 引数パック args に詰め込まれていて、後で使用することができる。 以下の呼び出しでは、
printf(++s, args...);引数パック args が展開されるので、次の引数は value として使えるようになる。 これは args が空になる (即ち最初のバージョンの printf() が呼び出される) まで続く。
もし関数型プログラミングに慣れているのなら、これはごく普通の書き方だと思うだろう。 そうでないなら、もうちょっと技術的な手がかりとなる例をお見せしよう。 まず、単純な可変個引数 template 関数 (上記の printf() のような) を宣言して使うことができる:
template<class ... Types> void f(Types ... args); // 可変個引数 template 関数 // (任意の型の可変個の引数を取る関数) f(); // OK: 引数なし f(1); // OK: 引数一つ: int f(2, 1.0); // OK: 引数二つ: int と double以下のように可変個型を作ることができる:
template<typename Head, typename... Tail> class tuple<Head, Tail...> : private tuple<Tail...> { // ここで再帰呼び出し // 基本的に、tuple はその先頭 (一番目の (型/値) ペア) に格納され // その後 (残りの (型/値) ペア) の tuple から派生する。 // 型の中に埋め込まれている型は、データとして格納されていないことに注意 typedef tuple<Tail...> inherited; public: tuple() { } // 既定: 空の tuple // 別々の引数から tuple を構築: tuple(typename add_const_reference<Head>::type v, typename add_const_reference<Tail>::type... vtail) : m_head(v), inherited(vtail...) { } // 別の tuple から tuple を構築: template<typename... VValues> tuple(const tuple<VValues...>& other) : m_head(other.head()), inherited(other.tail()) { } template<typename... VValues> tuple& operator=(const tuple<VValues...>& other) // 代入 { m_head = other.head(); tail() = other.tail(); return *this; } typename add_reference<Head>::type head() { return m_head; } typename add_reference<const Head>::type head() const { return m_head; } inherited& tail() { return *this; } const inherited& tail() const { return *this; } protected: Head m_head; }以上の定義から、tuple を作ること (やコピーしたり操作すること) ができる:
tuple<string,vector<int>,double> tt("hello",{1,2,3,4},1.2); string h = tt.head(); // "hello" tuple<vector<int>,double> t2 = tt.tail(); // {{1,2,3,4},1.2};これら全ての型を完全に扱うのは少し長ったらしくなりがちなので、標準ライブラリの make_tuple() を用いて、引数型を導出させる:
template<class... Types> tuple<Types...> make_tuple(Types&&... t) // この定義は多少単純化 (標準の 20.5.2.2 を参照) { return tuple<Types...>(t...); } string s = "Hello"; vector<int> v = {1,22,3,4,5}; auto x = make_tuple(s,v,1.2);
以下も参照:
string a[] = { "foo", " bar" }; // ok: 配列変数の初期化 vector<string> v = { "foo", " bar" }; // エラー: 集合体でない vector に対する初期化リスト void f(string a[]); f( { "foo", " bar" } ); // 文法エラー: ブロックを引数にしているそれから、
int a = 2; // 「代入形式」 int aa[] = { 2, 3 }; // リストを使った代入形式 complex z(1,2); // 「関数形式」の初期化 x = Ptr(y); // 「関数形式」の変換 / キャスト / 構築それに、
int a(1); // 変数定義 int b(); // 関数宣言 int b(foo); // 変数定義か関数宣言初期化の規約を憶えたり、どの方法が一番良いか選ぶのが難しくなりがちだ。
C++11 の解は {} 初期化リストを全ての初期化で可能にすることである:
X x1 = X{1,2}; X x2 = {1,2}; // この = がなくても良い X x3{1,2}; X* p = new X{1,2}; struct D : X { D(int x, int y) :X{x,y} { /* ... */ }; }; struct S { int a[3]; S(int x, int y, int z) :a{x,y,z} { /* ... */ }; // 古い問題の解決 };重要なことは、X{a} が同じ値を全ての文脈で構築するということであり、故に、{} 初期化は、それが合法である全ての場所で同一の結果を与える。 例えば:
X x{a}; X* p = new X{a}; z = X{a}; // キャストとして使用 f({a}); // (X 型の) 関数の引数 return {a}; // 関数の戻り値 (関数の戻り値型は X)
以下も参照。
void incr(int& a) { ++a; } int i = 0; incr(i); // i は 1 になる incr(0); // エラー: 0 は左辺値でないもし incr(0) が許されたなら、誰にも操作できない一時的な値が加算されてしまう。 あるいは - 更に拙いことに - 0 が1 になってしまう。 後者は馬鹿げた話のように聞こえるが、値 0 を持つアドレスを保持していた初期の Fortran コンパイラで、実際にそういうバグがあったのだ。
ここまでは良いとして、以下を考えよ。
template<class T> swap(T& a, T& b) // 「古いスタイルの swap」 { T tmp(a); // ここで a のコピーが二つ a = b; // ここで b のコピーが二つ b = tmp; // ここで tmp (つまり a) のコピーが二つ }T が、string や vector のように要素のコピーのコストが高い型である場合、swap のコストも高くなってしまう (標準ライブラリでは、これをうまく扱うために string や vector の swap() を特殊化している)。 ちょっと変だと思わないか? 我々は、別にコピーを作りたいわけではなく、ちょっと a、b、そして tmp の値を動かしたいだけなのだ。
C++11 では、引数をコピーでなく移動させる「移動コンストラクタ」と「移動代入」を定義することができる:
template<class T> class vector { // ... vector(const vector&); // コピーコンストラクタ vector(vector&&); // 移動コンストラクタ vector& operator=(const vector&); // コピー代入 vector& operator=(vector&&); // 移動代入 }; // メモ: 移動コンストラクタと移動代入は、非 const && を引数に取る // これらはその引数に書込でき、そして通常は書込に使う&& が「右辺値リファレンス」であることを示す。 右辺値リファレンスは右辺値を指すことができる (が、左辺値を指すことはできない):
X a; X f(); X& r1 = a; // r1 は a を指す (左辺値) X& r2 = f(); // エラー: f() は右辺値; 指すことはできない X&& rr1 = f(); // 大丈夫: rr1 は一時的な値を指す X&& rr2 = a; // エラー: 指そうとしている a は左辺値移動代入の背景にある考えは、コピーを行うことを代替しようというもので、単純に転送先の表現を安価な既定値で置き換えようというものだ。 例えば、s1=s2 の文字列で移動代入を使うと s2 の文字のコピーが必要なくなる; 代わりに、s1 は s2 の文字を自分自身の文字として扱い、元の文字は削除される (s2 に残るかも知れないが、破壊されるだけだろう)。
どうすれば単純に代入元から移動させてよいか判断できるだろう? コンパイラにこう伝えればよい:
template<class T> void swap(T& a, T& b) // 「完全な swap」(ほとんど) { T tmp = move(a); // a を破棄してよい a = move(b); // b を破棄してよい b = move(tmp); // tmp を破棄してよい }move(x) は「x を右辺値として扱ってよい」という意味だ。 もし move() が rval() を呼び出しているならこれだけで改善されるだろうが、これまで move() は何年も使われてきた。 move() template 関数を C++11 で右辺値リファレンスを使うように書き直せばよいのだ (「簡単な紹介」を参照)。
右辺値リファレンスは完全な転送として使うこともできる。
C++11 標準ライブラリでは、全てのコンテナで、移動コンストラクタと移動代入演算子が提供され、insert() や push_back() のように新しい要素を追加する操作で右辺値リファレンスを使うバージョンが追加される。 最終的に、コピーが減ることで、ユーザの預り知らないうちに標準コンテナとアルゴリズムの性能が向上する。
以下も参照。
union U { int m1; complex<double> m2; // エラー (変): complex にはコンストラクタがある string m3; // エラー (変ではない): string にはコンストラクタ、コピー、デストラクタで // 維持しなくてはならない重要な不変条件がある };特に、
U u; // 一体どのコンストラクタ? u.m1 = 1; // int メンバへの代入 string s = u.m3; // 失敗: string メンバから読み込んでしまった明らかに、あるメンバに書き込んで別のメンバを読むことは非合法であるが、それでもこうしてしまうのだ (普通は間違いで)。
C++11 は union の制限を修正して、より多くのメンバ型を実現可能にした; 特に、コンストラクタとデストラクタのあるメンバ型を許容するようになった。 また、峻別された union の構築を奨励することで、よりフレキシブルになった union がエラーになりにくいように制限を追加した。
union のメンバ型には以下の制限がある:
union U1 { int m1; complex<double> m2; // ok }; union U2 { int m1; string m3; // ok };これはエラーになりがちに見えるが、新しい制限が救ってくれる。 特に:
U1 u; // ok u.m2 = {1,2}; // ok: complex メンバへの代入 U2 u2; // エラー: string のデストラクタが、delete されるべき U2 のデストラクタになる U2 u3 = u2; // エラー: string のコピーコンストラクタが、delete されるべき U2 のコピーコンストラクタになるU2 は、どのメンバが使われているか追跡するものを構造体の中に埋め込まれない限り、基本的に使い道がない。 従って、以下のように union を峻別しよう:
class Widget { // union として表現された三つの実装 private: enum class Tag { point, number, text } type; // 峻別 union { // 表現 point p; // point にはコンストラクタがある int i; string s; // string には既定コンストラクタ、コピー演算子、デストラクタがある }; // ... Widget& operator=(const Widget& w) // string 版のために必要 { if (type==Tag::text && w.type==Tag::text) { s = w.s; // 通常の string 代入 return *this; } if (type==Tag::text) s.~string(); // 破棄 (明示的な!) switch (w.type) { case Tag::point: p = w.p; break; // 通常のコピー case Tag::number: i = w.i; break; case Tag::text: new(&s)(w.s); break; // 配置 new } type = w.type; return *this; } };以下も参照:
struct S { int a; }; // S は POD struct SS { int a; SS(int aa) : a(aa) { } }; // SS は POD でない struct SSS { virtual void f(); /* ... */ };C++11 では、S と SS は「標準レイアウト型」(又の名を POD) になる。 なぜなら SS には実際のところ何の「魔法」も掛かっていないからだ: コンストラクタはレイアウトに何の影響も与えず (従って memcpy() はうまく動作する)、単に初期化の規則を定めているだけだ (memset() は不変性を強制できないため、この点ではよろしくない)。 しかしながら、SSS には vptr があるから「plain old data」のようにはならない。 C++11 は、何を POD として扱うかという様々な技術的観点に基づいて、POD、自明にコピー可能な型、自明な型、標準レイアウト型を定義している。 POD は以下のように再帰的に定義される。
以下も参照:
string s = "\\w\\\\\\w"; // 多分正しいバックスラッシュ文字は、正規表現の中ではバックスラッシュ二つで表されることに注意すること。 基本的に、「生の文字列リテラル」は一つのバックスラッシュでバックスラッシュを表す文字列リテラルであり、そのため、上記の例は以下のようになる:
string s = R"(\w\\\w)"; // 間違いなく正しい生の文字列の動機となる例として、元々の提案にあった、
"('(?:[^\\\\']|\\\\.)*'|\"(?:[^\\\\\"]|\\\\.)*\")|" // 五つのバックスラッシュは正しいか否か? // 熟練者ですら容易に混乱する。が、良い例だ。 R"(...)" の表現の方が「生の」"..." より少し分かりやすいが、エスケープ文字が使えないと「生+α」が必要になることがある。 生の文字列中にクォートを混ぜるにはどうすればよいだろう? クォートが ) で始まらない場合は簡単だ:
R"("quoted string")" // 文字列は "quoted string"では、)" の文字の並びを生の文字列にしたい場合はどうすればよいだろう? 幸いなことに、これは滅多に起きない問題だが、("...") は既定の区切りペアに過ぎない。 "(...)" 中の (...) の先頭と末尾に区切りを追加することもできる。 例えば、
R"***("quoted string containing the usual terminator (")")***" // 文字列は "quoted string containing the usual terminator (")") の後の文字の並びは ( の前の並びと一致しなくてはならない。 この方法で (ほとんど) 任意の複雑なパターンに対処することができる。
生の文字列中の最初の R にはエンコーディング接頭辞を付けてもよい: u8、u、U、L。 例えば、u8R"(fdfdfa)" は UTF-8 文字列リテラルだ。
以下も参照。
123 // int 1.2 // double 1.2F // float 'a' // char 1ULL // unsigned long long 0xD0 // 十六進 unsigned "as" // 文字列しかしながら、C++98 には、ユーザ定義型用のリテラルはなかった。 これは悩みの種になりかねないし、更に、ユーザ定義型が組込型と同様にサポートされるべきだという原則に反するように思われる。 特に要望が多いのは以下のような場合だ:
"Hi!"s // string、『ゼロ終端の char 配列』ではない 1.2i // 虚数 123.4567891234df // 十進浮動小数 (IBM) 101010111000101b // 二進数 123s // 秒 123.56km // マイルではない! (単位) 1234567890123456789012345678901234567890x // 拡張精度C++11 は、お望みの型について、接尾語を追加することでリテラルになるリテラル演算子により、『ユーザ定義リテラル』をサポートする。 例えば:
constexpr complex<double> operator "" i(long double d) // 虚数リテラル { return {0,d}; // complex<double > はリテラル型 } std::string operator""s (const char* p, size_t n) // std::string リテラル { return string(p,n); // フリーストアの割り付けが必要 }コンパイル時評価を可能にする constexpr を使用していることに注意。 ここまで来れば、以下のように書くことができる。
template<class T> void f(const T&); f("Hello"); // const char* へのポインタを渡す f("Hello"s); // (5 文字の) string オブジェクトを渡す f("Hello\n"s); // (6 文字の) string オブジェクトを渡す auto z = 2+1i; // complex<double>(2,1)基本的な (実装の) 考えは、何がリテラルになり得るかパースした後、常にコンパイラが接尾語をチェックするというものだ。 ユーザ定義リテラルの仕組みで、新しい接尾語をユーザに指定させ、ユーザがリテラルで何かする前に何をすべきか示すことが単純に可能になる。 組込リテラル接尾語の意味やリテラル引数の文法再定義することはできない。 リテラル演算子は、その (先行する) リテラルを『処理済の』形式 (新しい接尾語が定義されていない場合に渡されるはずの値) か、『元々の』形式 (文字列として) で要求することができる。
『元々の』文字列を得るには、単に一つの const char* 引数を要求すればよい:
Bignum operator"" x(const char* p) { return Bignum(p); } void f(Bignum); f(1234567890123456789012345678901234567890x);ここで、C 形式の文字列 "1234567890123456789012345678901234567890" が operator"" x() に渡される。 数字を明示的に文字列にしていない点に注意すること。
ユーザ定義リテラルをつくる接尾語には四種類ある。
string operator"" S(const char* p); // 警告: 期待したようには動かない "one two"S; // エラー: 適用可能なリテラル演算子がないこの論拠は、『異なる種類の文字列』を持とうとするなら、いずれにせよほとんどの場合に文字数を知りたいだろうということだ。
接尾語は短くなる傾向が強いだろうから (例えば、string の s、虚数の i、メートルの m、拡張数の x 等)、異なる用法が簡単に衝突してしまう。 衝突を避けるには名前空間を使う。
namespace Numerics { // ... class Bignum { /* ... */ }; namespace literals { operator"" X(char const*); } } using namespace Numerics::literals;
以下も参照:
void f [[ noreturn ]] () // f() は決して return しない { throw "error"; // OK } struct foo* f [[carries_dependency]] (int i); // 最適化のヒント int* g(int* x, int* y [[carries_dependency]]);上記の通り、アトリビュートは二重カギ括弧 [[ ... ]] の中に置かれる。 noreturn と align は標準で定義される二つのアトリビュートだ; アトリビュートは言語の方言として使われるのではないか、というもっともらしい危惧がある。 アトリビュートは、プログラムの意味に影響を与えず、エラーを検知したり ([[noreturn]] のように)、最適化を支援する ([[carries_dependency]] のように) 用途でだけ使われるべきだ。
アトリビュートの用途として計画されているものの中に OpenMP のサポート向上がある。 例えば:
for [[omp::parallel()]] (int i=0; i<v.size(); ++i) { // ... }このように、アトリビュートを修飾することもできる。
以下も参照:
vector<int> v = {50, -10, 20, -30}; std::sort(v.begin(), v.end()); // 既定の sort // ここで、 v は { -30, -10, 20, 50 } になる // 絶対値による sort: std::sort(v.begin(), v.end(), [](int a, int b) { return abs(a)<abs(b); }); // ここで、 v は { -10, 20, -30, 50 } になる引数 [](int a, int b) { return abs(a)<abs(b); } が「ラムダ」 (または「ラムダ関数」や「ラムダ式」とも) であり、与えられた二つの整数の引数 a と b の絶対値を比較した結果を返す。
ラムダ式は、自身が使われているスコープ内の局所変数にアクセスすることもできる:
void f(vector<Record>& v) { vector<int> indices(v.size()); int count = 0; generate(indices.begin(),indices.end(),[&count](){ return count++; }); // Record の項目 name 順に indices を並べる: std::sort(indices.begin(), indices.end(), [&](int a, int b) { return v[a].name<v[b].name; }); // ... }人によってはこれを「マジでカッコいい!」と考えるかも知れないが、人によっては危険で分かりにくいコードを書く方法だと思うかも知れない。 個人的には、どちらも正しいと思う。
[&] は、リファレンスによって渡される局所変数を指定する「キャプチャリスト」である。 v だけを「キャプチャ」したいのなら [&v] と書けばよい。 v を値で受け取りたい場合は、[=v] と書く。 何もキャプチャしない場合は []、全部リファレンスでキャプチャする場合は [&]、全部を値でキャプチャする場合は [=] となる。
動作が一般的でも単純でもないのであれば、著者としては名前付きの関数オブジェクトか関数の使用をお勧めする。 例えば、上記の例は以下のように書くこともできる:
void f(vector<Record>& v) { vectorこの Record の name 項目の比較のように、ごく小さい関数では、関数オブジェクトで書いた方が冗長になるが、生成されるコードは多分同じだろう。 C++98 では、このような関数オブジェクトを template 引数にするために非局所化する必要があったが、C++11 ではもうその必要はなくなった。indices(v.size()); int count = 0; generate(indices.begin(),indices.end(),[&](){ return ++count; }); struct Cmp_names { const vector<Record>& vr; Cmp_names(const vector<Record>& r) :vr(r) { } bool operator()(int a, int b) const { return vr[a].name<vr[b].name; } }; // Record の項目 name 順に indices を並べる: std::sort(indices.begin(), indices.end(), Cmp_names(v)); // ... }
ラムダを定義するには、以下のものを与える必要がある。
void f(vector<X>& v) { struct Less { bool operator()(const X& a, const X& b) { return a.v<b.v; } }; sort(v.begin(), v.end(), Less()); // C++98: エラー: Less は局所型 // C++11: ok }C++11 では、ラムダ式 を用いて以下のように書くこともできる:
void f(vector<X>& v) { sort(v.begin(), v.end(), [] (const X& a, const X& b) { return a.v<b.v; }); // C++11 }名前を付けるということは、ドキュメント化に大変便利になり得るし、良い設計を促すことになるという点も意識しておく価値がある。 また、非局所 (名前が必要な) エンティティであれば再利用することもできる。
C++11 は無名型の値を template 引数として使用することも許容する:
template<typename T> void foo(T const& t){} enum X { x }; enum { y }; int main() { foo(x); // C++98: ok; C++11: ok foo(y); // C++98: エラー; C++11: ok enum Z { z }; foo(z); // C++98: エラー; C++11: ok }以下も参照:
extern "C" double sqrt(double) noexcept; // 決して例外を投げない vector<double> my_computation(const vector<double>& v) noexcept // メモリ不足に対応するようにはできてないよ { vector<double> res(v.size()); // 例外を投げるかも for(int i; i<v.size(); ++i) res[i] = sqrt(v[i]); return res; }noexcept と宣言された関数が例外を投げると (つまり、その例外が noexcept 関数から抜け出そうとすると)、そのプログラムは (terminate() の呼出によって) 終了する。 terminate() の呼出では、オブジェクトが正常な状態であることをアテにできない (すなわち、デストラクタが起動されている保証や、スタックが巻き戻されている保証はないし、問題が起きなかったかのようにプログラムを再開できる可能性はゼロだ)。 これはよくよく考えられた上でのことで、noexcept を単純で、乱暴だが、非常に効率的な (昔の動的な throw() よりはるかに効率的だ) 仕組みにしたのだ。
ある関数を条件付きで noexcept にすることもできる。 例えば、あるアルゴリズムで用いる template 引数が noexcept の場合に限り、noexcept と指定することができる:
template<class T> void do_f(vector<T>& v) noexcept(noexcept(f(v.at(0)))) // f(v.at(0)) が可能なら例外を投げてよい { for(int i; i<v.size(); ++i) v.at(i) = f(v.at(i)); }ここで、最初の noexcept は演算子として使用した: noexcept(f(v.at(0))) は f(v.at(0)) が例外を投げられない場合、つまり f() と at() が noexcept の場合に真となる。
noexcept() 演算子は定数式であり、その演算対象が評価されることはない。
一般的な noexcept 宣言の形式は noexcept(式) と、noexcept(true) の単純な省略形である『noexcept』だ。 ある関数の全ての宣言は noexcept の仕樣に関して互換でなければならない。
デストラクタは例外を投げるべきではない; 自動生成されたデストラクタは、その class の全てのメンバが noexcept のデストラクタを持つ場合、暗黙的に noexcept となる (そのコードの内容とは関係なく)。
例外を投げる移動操作は良くないアイディアの典型なので、可能ならどこでもそれらを noexcept と宣言すること。 自動生成されたコピーや移動操作は、その class の全てのメンバが noexcept のデストラクタを持つ場合、暗黙的に noexcept となる。
noexcept は、性能を向上させ要件を明確化させるために標準ライブラリの中で広範かつ体系的に利用されている。 以下も参照:
alignas(double) unsigned char c[1024]; // 文字配列、double 境界に整列させる alignas(16) char[100]; // 16 バイト境界に整列させるその演算対象 (型でなければならない) のアラインメントを返す alignof 演算子もある。 例えば、
constexpr int n = alignof(int); // int は n バイト境界に割り付けられる
以下も参照:
struct B { virtual void f(); virtual void g() const; virtual void h(char); void k(); // 仮想関数ではない }; struct D : B { void f(); // B::f() をオーバーライド void g(); // B::g() をオーバーライドしない (型が間違っている)) virtual void h(char); // B::h() をオーバーライド void k(); // B::k() をオーバーライドしない (B::k() は仮想関数でない) };これは混乱の元だし (プログラマは何を意図していたのだろうか?) コンパイラが疑わしいコードに対して警告を発するかどうかで問題になる。 例えば、
struct D : B { void f() override; // OK: B::f() をオーバーライド void g() override; // エラー: 型が間違っている virtual void h(char); // B::h() をオーバーライド、警告されるだろう void k() override; // エラー: B::k() は仮想関数でない };override とマークされた宣言は、その関数をオーバーロードした時に限り有効になる。 h() の問題が検出される保証はない (言語仕様に則ればエラーではないから) が、診断するのは容易だろう。
override は文脈キーワードに過ぎないので、まだ識別子として使うことができる:
int override = 7; // 推奨できない以下も参照:
struct B { virtual void f() const final; // オーバーライドしないこと virtual void g(); }; struct D : B { void f() const; // エラー: D::f が final な B::f をオーバーライドしようとした void g(); // OK };オーバーライドを禁止したいという要求にはもっともな理由があるのだが、著者が見てきた final が必要というデモンストレーションのほとんどは、いかに virtual 関数のコストが高いかという間違った仮定に基づいていた。 そういうわけで、もし final 指定子を追加しようとする衝動に駆られたら、どうかその理由が正当なものかダブルチェックして欲しい: 誰かが定義した class が仮想関数を上書きすると意味論的なエラーが発生するのか? final を追加することで、思いもよらないもっと優れた関数の実装の基になったかも知れない class の、未来のユーザの可能性を閉ざしてしまうのだ。 もし開放性の維持を望まないというのなら、なぜ最初にその関数を virtual と定義したのか? この問いに対して、著者が出会ったもっとも説得力のある答えは以下のようなものだ: これはフレームワークの基本的な関数で、フレームワークベンダはオーバーライドする必要があるが、一般的なユーザがオーバーライドするのは安全でない、と。 著者の偏見はこの種の要求に懐疑的なのだが。
性能上の理由 (inline 化) とか、単純に絶対オーバーライドさせたくないだけなら、最初にその関数を virtual と定義しないのがお決まりのより良い答えだ。 C++ は Java じゃないんだから。
finla は文脈キーワードに過ぎないので、まだ識別子として使うことができる:
int final = 7; // 推奨できない
以下も参照:
#define report(test, ...) ((test)?puts(#test):printf(_ _VA_ARGS_ _))
以下も参照:
以下を参照。
try { throw e; } catch(...) { return current_exception(); }これは、特にあるスレッドからの例外を他のスレッドへ送出する際に便利だ。
#include "MyVector.h" extern template class MyVector<int>; // 以降で暗黙的にインスタンス化されるのを抑止 -- // MyVector<int> はどこか別の場所で明示的にインスタンス化 void foo(MyVector<int>& v) { // ここで vector を使う }『どこか別の場所』には以下のように書かれているだろう:
#include "MyVector.h" template class MyVector<int>; // MyVectorこれは基本的に、コンパイラとリンカによる大きな重複作業を避けるためのものだ。をクライアント (例えば共有ライブラリ) から利用可能にする
以下も参照。
// ファイル V99.h: inline namespace V99 { void f(int); // V98 版よりも巧く何かを行う void f(double); // 新機能 // ... } // ファイル V98.h: namespace V98 { void f(int); // 何かを行う // ... } // ファイル Mine.h: namespace Mine { #include "V99.h" #include "V98.h" }ここで、namespace Mine には、最新版 (V99) と一つ古い版 (V98) の両方がある。 どちらか特定したいのであれば、以下のようにできる:
#include "Mine.h" using namespace Mine; // ... V98::f(1); // 旧版 V99::f(1); // 新版 f(1); // 既定版要点は、inline 指定子で、重箱になった namespace からの宣言が、あたかも閉じた namespace の中で宣言したかのようになる、ということだ。
namespace の設計者が書く場所に置かれるため、inline 指定子は、極めて『静的』で、実装者指向の機能であり、全てのユーザに選択を強制する。 Mine のユーザが『V99 でなく V98 を既定にしたい』と言うことはできないのだ。
以下も参照。
struct S { S(int); }; // 「普通のコンストラクタ」で暗黙の変換を定義 S s1(1); // ok S s2 = 1; // ok void f(S); f(1); // ok (だが、悪い意味でびっくりすることも -- もし S が vectorだったら?) struct E { explicit E(int); }; // 明示的なコンストラクタ E e1(1); // ok E e2 = 1; // エラー (だが、びっくりするかも) void f(E); f(1); // エラー (びっくりしないですむ -- int から std::vector へのコンストラクタが explicit なのと同様)しかしながら、コンストラクタは変換を定義する唯一の仕組みではない。 class を修正できない場合、別の class からの変換演算子を定義することもできる。 例えば:
struct S { S(int) { } /* ... */ }; struct SS { int m; SS(int x) :m(x) { } operator S() { return S(m); } // S には S(SS) がないため; 非侵入的 }; SS ss(1); S s1 = ss; // ok; 暗黙的コンストラクタのように振る舞う S s2(ss); // ok; 暗黙的コンストラクタのように振る舞う void f(S); f(ss); // ok; 暗黙的コンストラクタのように振る舞う残念ながら、explicit 変換演算子は存在しない (ごくわずかながら問題のある例があるため)。 C++11 は explicit となるべき変換演算子を許容することに伴う間違いを防ぐことができる。 例えば:
struct S { S(int) { } }; struct SS { int m; SS(int x) :m(x) { } explicit operator S() { return S(m); } // S には S(SS) がないため }; SS ss(1); S s1 = ss; // エラー; 明示的コンストラクタのように振る舞う S s2(ss); // ok ; 明示的コンストラクタのように振る舞う void f(S); f(ss); // エラー; 明示的コンストラクタのように振る舞う以下も参照:
bool all_of(Iter first, Iter last, Pred pred); bool any_of(Iter first, Iter last, Pred pred); bool none_of(Iter first, Iter last, Pred pred); Iter find_if_not(Iter first, Iter last, Pred pred); OutIter copy_if(InIter first, InIter last, OutIter result, Pred pred); OutIter copy_n(InIter first, InIter::difference_type n, OutIter result); OutIter move(InIter first, InIter last, OutIter result); OutIter move_backward(InIter first, InIter last, OutIter result); pair<OutIter1, OutIter2> partition_copy(InIter first, InIter last, OutIter1 out_true, OutIter2 out_false, Pred pred); Iter partition_point(Iter first, Iter last, Pred pred); RAIter partial_sort_copy(InIter first, InIter last, RAIter result_first, RAIter result_last); RAIter partial_sort_copy(InIter first, InIter last, RAIter result_first, RAIter result_last, Compare comp); bool is_sorted(Iter first, Iter last); bool is_sorted(Iter first, Iter last, Compare comp); Iter is_sorted_until(Iter first, Iter last); Iter is_sorted_until(Iter first, Iter last, Compare comp); bool is_heap(Iter first, Iter last); bool is_heap(Iter first, Iter last, Compare comp); Iter is_heap_until(Iter first, Iter last); Iter is_heap_until(Iter first, Iter last, Compare comp); T min(initializer_list<T> t); T min(initializer_list<T> t, Compare comp); T max(initializer_list<T> t); T max(initializer_list<T> t, Compare comp); pair<const T&, const T&> minmax(const T& a, const T& b); pair<const T&, const T&> minmax(const T& a, const T& b, Compare comp); pair<const T&, const T&> minmax(initializer_list<T> t); pair<const T&, const T&> minmax(initializer_list<T> t, Compare comp); pair<Iter, Iter> minmax_element(Iter first, Iter last); pair<Iter, Iter> minmax_element(Iter first, Iter last, Compare comp); void iota(Iter first, Iter last, T value); // 範囲 [first,last) のイテレータ i で参照される各要素について、代入 *i = value が行われ、++value の加算が行われる
移動を利用することで、『スマート』ポインタ、特に unique_ptr を格納したコンテナの並べ替え (や、その他のアルゴリズム) が単純かつ効率的になることも考慮されたい:
template<class P> struct Cmp<P> { // *P の値を比較 bool operator() (P& a, P& b) const { return *a<*b; } } vector<std::unique_ptr<Big>> vb; // Big オブジェクトへの unique_ptr で vb を埋める sort(vb.begin(),vb.end(),Cmp<unique_ptr<Big>>()); // これを auto_ptr でやってはならない
sort(vb.begin(),vb.end(),[](unique_ptr<Big> a, unique_ptr<Big> b) { return *a<*b; });著者は、ラムダが最初のうちはちょっと過剰に使われると予想している (全ての強力な機能がそうであるように)。
auto x = max({x,y,z},Nocase());
vector<string> vs = { "Hello", ", ", "World!", "\n" }; for (auto s : vs ) cout << s;
vector<int> make_random(int n) { vector<int> ref(n); for(auto& x : ref) x = rand_int(0,255); // 何らかの乱数生成器 return ref; } vector<int> v = make_random(10000); for (auto x : make_random(1000000)) cout << x << '\n';ここでの要点は vector がコピーされないということだ。 これを自由記憶領域に割り付けた vector の返却で書き直そうとすると、メモリ管理とお付き合いする必要がある。 これを make_random() の引数に vector を渡すことで書き直そうとすると、遥かに分かりにくく (そして間違う可能性が高く) なるだろう。
vector<pair<string,int>> vp; string s; int i; while(cin>>s>>i) vp.push_back({s,i});これは pair<string,int> を s と i から構築して、vp へ移動させる。 『コピー』でなく『移動』していることに注意; 右辺値リファレンス を引数にとるバージョンの push_back() ができたので、string の移動コンストラクタの利点を享受できる。 また、統一された初期化構文により、冗長でなくなっていることにも注意されたい。
vector<pair<string,int>> vp; string s; int i; while(cin>>s>>i) vp.emplace_back(s,i);据付は 可変個引数 template を引数に取り、お望みの型のオブジェクトを構築する。 emplace_back() が本当に push_back() より効率的かどうかは、中身の型と (ライブラリと可変個引数 template の) 実装に依存する。 それが気になるなら計測すればいい。 あるいは、綺麗だと思う方を選ぶこと: vp.push_back({s,i}); か vp.emplace_back(s,i); かだ。 今のところ、著者は push_back() の方が好きだが、時間が経てば変わるかも知れない。
template<class T> class Simple_allocator { // C++98 形式 // データなし // 普通のアロケータ }; class Arena { void* p; int s; public: Arena(void* pp, int ss); // p[0..ss-1] から割り付ける }; template<class T> struct My_alloc { Arena& a; My_alloc(Arena& aa) : a(aa) { } // 普通のアロケータ }; Arena my_arena1(new char[100000],100000); Arena my_arena2(new char[1000000],1000000); vector<int> v0; // 既定のアロケータを使う割付 vector<int,My_alloc<int>> v1(My_alloc<int>{my_arena1}); // my_arena1 を使う割付 vector<int,My_alloc<int>> v2(My_alloc<int>{my_arena2}); // my_arena2 を使う割付 vector<int,Simple_alloc<int>> v3; // Simple_alloc を使う割付概ね、この冗長さは typedef で軽減されるだろう。
既定のアロケータと Simple_alloc が vector オブジェクトの空間を使い切らない保証はないが、エレガントな template メタプログラミングをライブラリの中でちょっと効かせるだけで、保証させることもできる。 従って、アロケータ型の使用が空間的なオーバーヘッドを強いるのは、そのオブジェクトが実際に状態を持つ場合に限られる (My_alloc のように)。
むしろ隠れた問題は、コンテナとユーザ定義アロケータを使う時に起きるだろう: コンテナと同じ割付領域に要素を配置すべきだろうか? 例えば、もしあなたが Your_allocator を Your_string の要素を割り付けるのに使い、私が My_allocator を My_vector の要素を割り付けるのに使った場合、My_vector<Your_allocator> の string の要素の割付にはどちらのアロケータが使われるだろう? 解決方法は、要素へ渡すアロケータが何か、コンテナに伝えられるようにすることだ。 例えば、アロケータ My_alloc があり、vector<string> に対して My_alloc で vector の要素と string の要素両方を割り付けたいとしよう。 まず、My_alloc オブジェクトを受け取れる string をつくる必要がある:
using xstring = basic_string<char, char_traits<char>, My_alloc<char>>; // 私のアロケータを使う string次に、この文字列を受け取り、My_alloc オブジェクトを受け取り、文字列へのオブジェクトを渡せる vector をつくらなければならない:
using svec = vector<xstring,scoped_allocator_adaptor<My_alloc<xstring>>>;ここまでやれば、My_alloc<xstring> 型のアロケータをつくることができる:
svec v(svec::allocator_type(My_alloc<xstring>{my_arena1}));これで svec は My_alloc を文字列のメモリ割付に使う string の vector になった。 string も My_alloc を使うように指示するのが、標準ライブラリの『アダプタ』 (『ラッパー型』) scoped_allocator_adaptor の新しい点だ。 このアダプタは (自明に) My_alloc<xstring> を xstring に必要な My_alloc<char> へ変換できることに注意せよ。
従って、四種類の変種ができる:
// vector と string は各々の (既定の) アロケータを使う: using svec0 = vector<string>; svec0 v0; // vector (だけ) は My_alloc を使い、string は自身の (既定) アロケータを使う: using svec1 = vector<string,My_alloc<string>>; svec1 v1(My_alloc<string>{my_arena1}); // vector と string は My_alloc (上述の通り) を使う: using xstring = basic_string<char, char_traits<char>, My_alloc<char>>; using svec2 = vector<xstring,scoped_allocator_adaptor<My_alloc<xstring>>>; svec2 v2(scoped_allocator_adaptor<My_alloc<xstring>>{my_arena1}); // vector は My_alloc を使い、string は My_string_alloc を使う: using xstring2 = basic_string<char, char_traits<char>, My_string_alloc<char>>; using svec3 = vector<xstring2,scoped_allocator_adaptor<My_alloc<xstring>, My_string_alloc<char>>>; svec3 v3(scoped_allocator_adaptor<My_alloc<xstring2>, My_string_alloc<char>>{my_arena1,my_string_arena});明らかに、最初の変種 svec0 は、極めて最も一般的だろうが、メモリ関連の制約が厳しいシステムでは、他のバージョン (特に svec2) の方が重要になるかも知れない。 ちょっとした typedef で少しコードが読みやすくなるだろうが、ありがたいことに毎日書くようなものでもない。 scoped_allocator_adaptor2 は、二種類の非既定アロケータ用の scoped_allocator_adaptor の変種である。
以下も参照:
array<int,6> a = { 1, 2, 3 }; a[3]=4; int x = a[5]; // x は、既定の要素がゼロ初期化されるため、0 になる int* p1 = a; // エラー: std::array は暗黙的にポインタへ変換されない int* p2 = a.data(); // ok: 最初の要素へのポインタ長さゼロの array も可能だが、初期化リストから array の長さを導出することはできない:
array<int> a3 = { 1, 2, 3 }; // エラー: 大きさが未知/指定なし array<int,0> a0; // ok: 要素なし int* p = a0.data(); // 未既定。試さないこと標準の array の機能は組込システムプログラミング (や、同じ様に、制限が厳しかったり、性能要件が重要だったり、安全性が重要なタスク) で魅力的なものになる。 これは連続したコンテナなので、普通のメンバ型やメンバ関数の元にできる (ちょうど vector のように):
template<class C> C::value_type sum(const C& a) { return accumulate(a.begin(),a.end(),0); } array<int,10> a10; array<double,1000> a1000; vector<int> v; // ... int x1 = sum(a10); double x2 = sum(a1000); int x3 = sum(v);また、(潜在的に問題になりがちな) 派生型から基底型への変換はできない:
struct Apple : Fruit { /* ... */ }; struct Pear : Fruit { /* ... */ }; void nasty(array<Fruit*,10>& f) { f[7] = new Pear(); }; array<Apple*,10> apples; // ... nasty(apples); // エラー: array<Apple*,10> から array<Fruit*,10> へは変換できないもしこれが許されたなら、apples の中に Pear を含んでしまいかねない。
以下も参照:
template <ValueType T, Allocator Alloc = allocator<T> > requires NothrowDestructible<T> class forward_list { public: // 通常のコンテナ // size() なし // 逆方向への進行なし // back() も push_back() もなし };
以下も参照:
基本的な考え方は、最適化が可能かつもっともらしい場合に、単純に unordered_map を map の最適化版として使うことだ。 例えば:
map<string,int> m { {"Dijkstra",1972}, {"Scott",1976}, {"Wilkes",1967}, {"Hamming",1968} }; m["Ritchie"] = 1983; for(auto x : m) cout << '{' << x.first << ',' << x.second << '}'; unordered_map<string,int> um { {"Dijkstra",1972}, {"Scott",1976}, {"Wilkes",1967}, {"Hamming",1968} }; um["Ritchie"] = 1983; for(auto x : um) cout << '{' << x.first << ',' << x.second << '}';m のイテレータは要素をアルファベット順に表示するが、um はそうならない (偶然並ぶ場合を除いて)。 検索処理は、m と um とで全く違う実装になる。 m の場合、検索処理は log2(m.size()) 回の小なり不等号の比較分かかるのに対して、um の場合は一回のハッシュ関数の呼び出しと一回以上の等号比較になる。 要素の数が多くなければ (数十個くらい)、どちらが早いとも言い難い。 もっと要素の数が多くなれば (数千個とか)、unordered_map の検索の方が map より遙かに速くなるかも知れない。
要追記。
以下も参照:
tuple の要素型は明示的に指定することも導出する (make_tuple を使って) こともでき、get() を使って添え字 (ゼロ基点) で要素にアクセスできる:
tuple<string,int> t2("Kylling",123); auto t = make_tuple(string("Herring"),10, 1.23); // t は tuple<string,int,double> 型になる string s = get<0>(t); int x = get<1>(t); double d = get<2>(t);tuple は、コンパイル時に要素の異種混合リストが必要だが、それを保持する名前付きクラスを定義したくはない時なら、いつでも (直接的にせよ間接的にせよ) 使われる。 例えば、tuple は std::function と std::bind 内部で引数を保持するのに使われている。
もっとも頻繁に使われる tuple は 2-tuple、すなわち pair だ。 しかしながら、pair は std::pair (20.3.3 pair) として標準ライブラリで直接サポートされている。 pair を tuple の初期化に使うことはできるが、その逆はできない。
比較演算子 (==, !=, <, <=, >, and >=) は、比較可能な要素型の tuple では定義される。
以下も参照:
int f(int,char,double); auto ff = bind(f,_1,'c',1.2); // 戻り値型の導出 int x = ff(7); // f(7,'c',1.2);この引数の束縛は、普通『カリー化 (原文: Currying)』と呼ばれる。 _1 は、f が ff 経由で呼び出されたときに、ff の第一引数を渡すことを指示するプレースホルダオブジェクトだ。 第一引数は _1、第二引数は _2、等となる。 例えば:
int f(int,char,double); auto frev = bind(f,_3,_2,_1); // 引数の順を逆にする int x = frev(1.2,'c',7); // f(7,'c',1.2);auto のおかげで bind の結果の型を指定しなくてすんでいることに注意。
オーバーロードされた関数では、どの関数を束縛するか明示する必要があるため、引数を単純に束縛することはできない:
int g(int); double g(double); // g() はオーバーロードされている auto g1 = bind(g,_1); // エラー: どの g()? auto g2 = bind((double(*)(double))g,_1); // ok (だが醜い)bind() には二つの変種がある: 一つ目の変種は上述したものだが、戻り値型を明示的に指定する「古い」バージョンだ。
auto f2 = bind<int>(f,7,'c',_1); // 明示的な戻り値型 int x = f2(1.2); // f(7,'c',1.2);二つ目のバージョンは、一つ目の (ユーザにとってもっとも単純な) バージョンが C++98 では実装できなかったために必要だったもので、広く使われている。
function は、(...) 構文で呼び出せるものなら何でも値として保持できる型だ。 特に、bind の結果を function に代入できる。 function の使い方は非常に単純だ。 例えば:
function<float (int x, int y)> f; // 関数オブジェクトを作成 struct int_div { // () で呼び出せる何か float operator()(int x, int y) const { return ((float)x)/y; }; }; f = int_div(); // 代入 cout << f(5, 3) << endl; // 関数オブジェクト経由で呼び出し std::accumulate(b,e,1,f); // 美しい渡し方メンバ関数は余分な引数を持つ自由関数として扱うことができる。
struct X { int foo(int); }; function<int (X*, int)> f; f = &X::foo; // メンバへのポインタ X x; int v = f(&x, 5); // x の X::foo() を引数 5 で呼び出し function<int (int)> ff = std::bind(f,&x,_1); // f の最初の引数は &x v=ff(5); // x.foo(5) の呼び出しfunction はコールバックや引数として操作を受け渡したりするのに便利だ。 C++98 の標準ライブラリ関数オブジェクト mem_fun_t や pointer_to_unary_function 等の代わりにも使えるだろう。 同様に、bind() は bind1() や bind2() の代わりになるだろう。
以下も参照:
次に挙げるのは例外安全でないコードの伝統的な断片だ:
X* f() { X* p = new X; // 何かをする - そして例外が発生するかも知れない return p; }自由記憶上のオブジェクトへのポインタを unique_ptr に持つのが解決法だ:
X* f() { unique_ptr<X> p(new X); // {new X} としても良いが、= new X はダメ // 何かをする - そして例外が発生するかも知れない return p.release(); }これで、もし例外が発生しても、unique_ptr が (暗黙的に) 指しているオブジェクトを削除してくれる。 これは基本的な RAII だ。 しかしながら、本当に組込のポインタを返す必要がない限り、unique_ptr のまま返した方がいいだろう:
unique_ptr<X> f() { unique_ptr<X> p(new X); // {new X} としても良いが、= new X はダメ // 何かをする - そして例外が発生するかも知れない return p; // 所有権が f() の外へ渡る }f は以下のように使う:
void g() { unique_ptr<X> q = f(); // 移動コンストラクタを使う移動 q->memfct(2); // q を使う X x = *q; // 指している先のオブジェクトをコピー // ... } // q と q が指すオブジェクトは、抜けるときに破棄されるunique_ptr には『移動セマンティクス』があるため、f() の呼出結果の右辺値による q の初期化では、単純に q へ所有権が移るだけだ。
unique_ptr の使い道の一つに、コンテナへのポインタの格納がある。 これは、組込のポインタと同様だが、例外安全性に関する問題はない (更に、ポインタが指す要素の破棄の保証もできる):
vector<unique_ptr<string>> vs { new string{"Doug"}, new string{"Adams"} };
unique_ptr は単純な組込のポインタで表現されており、組込のポインタに対するオーバーヘッドは無視できる程度だ。 特に、unique_ptr はいかなる形の動的チェックも提供しない。
以下も参照
void test() { shared_ptr<int> p1(new int); // カウンタは 1 { shared_ptr<int> p2(p1); // カウンタは 2 { shared_ptr<int> p3(p1); // カウンタは 3 } // 参照カウントが 2 に戻る } // 参照カウントが 1 に戻る } // ここで参照回数が 0 になり、int が削除される。もっと現実的な例は、一般的なグラフにおいて、ノードへのポインタを破棄する際に、そのノードを指すポインタが他にあるか分からないような場合の、ノードを指すポインタだろう。 ノードがデストラクタの動作を必要とするリソース (例えば、ノードが破棄される時に閉じる必要があるファイルなら、ファイルハンドル) を保持しているかもしれない場合だ。 shared_ptr をガベージコレクタプラグインのようなものだと考えても良いが、それは十分に経済的なくらいガベージが少ないか、その実行環境でガベージコレクタが使えないか、あるいは、管理するリソースがメモリだけでない (例えばファイルハンドル) 場合に限られる。 例えば:
struct Node { // メモ: ノードは複数の他のノードから参照されうる。 shared_ptr<Node> left; shared_ptr<Node> right; File_handle f; // ... };ここで Node のデストラクタ (暗黙的に生成されるデストラクタでよい) はそのサブノードを破棄する。 すなわち、left と right のデストラクタが呼び出される。 left は shared_ptr なので、left が最後のポインタであれば、その先の Node (何であろうと) が破棄される。 right も同様に扱われ、f のデストラクタはたとえ f を必要とするものがあっても呼び出される。
単に所有権を左から右へ移すだけなら、shared_ptr を使うべきでないことに注意されたい; そのために unique_ptr があるのであって、unique_ptr の方がより安価で優れている。 ファクトリ関数やその類から値を戻すためにカウンタ付きポインタを使っているのなら、shared_ptr よりも unique_ptr へのアップグレードを考えた方がよい。
メモリリークを防ごうとして何の考えもなしにポインタを shared_ptr で置き換えるのはやめて欲しい; shared_ptr は万能薬ではないし、代償が伴うのだ:
以下も参照
void owner() { // ... vector<shared_ptr<Asteroid>> va(100); for (int i=0; i<va.size(); ++i) { // ... 新しい隕石用に周りの隕石との計算を行う ... va[i].reset(new Asteroid(weak_ptr<Asteroid>(va[neighbor])); launch(i); } // ... }reset() は新しいオブジェクトを参照する shared_ptr を作りだす関数だ。
見ての通り、『owner』は、各々の新しい Asteroid を単なる neighbor の一つとして与えるだけに、極度に単純化された。 カギは、隕石の weak_ptr を neighbor へ渡したことだ。 owner は、Asteroid がいつ参照されようと (ただし、それ以外では真でない) 共有所有権を表す shared_ptr を保持し続ける。 Asteroid の衝突計算は以下のようになるだろう:
void collision(weak_ptr<Asteroid> p) { if (auto q = p.lock()) { // p.lock は p のオブジェクトへの shared_ptr を返す // ... この隕石はまだ存在する: 計算... } else { // ... おおっと: この隕石はすでに破壊されている: これは放っておいて良い (これを指す weak_ptr の破棄...) } }もし owner がゲームを終了して全ての Asteroid を delete することになった (所有権を表す shared_ptr を破棄することで) としても、衝突計算中の Asteroid は全て正しく終了することに注意すること (なぜなら p.lock() の後で、無効にならない shared_ptr があるから)。
著者は、weak_ptr の利用が、『生の』shared_ptr の利用頻度よりはるかに少なくなると予想しており、unique_ptr が shared_ptr の利用頻度よりもはるかに多くなって欲しいと考えている。 unique_ptr はより単純な (そしてより効率的な) 所有権の書き表し方であり (ゆえに) より良い局所性を意味することになるからだ。
以下も参照
ポインタと寿命に関するルールは「安全に導き出されたポインタ」(3.7.4.3) の節で説明されているが、これを大まかにいうと「new で割り付けられた何かかそのサブオブジェクトを指すポインタ」のことだ。 ここで「安全に導き出されていないポインタ」、あるいは「特徴的な (disguised) ポインタ」と呼ばれたりする、あなたが良い振る舞いで一般人にわかりやすいと考えることを望むプログラムの中ではやらないようなもの、の例をいくつか挙げる:
int* p = new int; p+=10; // ... ここでコレクタが動くかも知れない ... p-=10; *p = 10; // この int はまだここにあると確信できる?
int* p = new int; int x = reinterpret_cast<int>(p); // 移植性に欠ける p=0; // ... ここでコレクタが動くかも知れない ... p = reinterpret_cast<int*>(x); *p = 10; // この int はまだここにあると確信できる?
プログラマは、(例えば想像等で) 発見可能なポインタがない場所や、コレクタがメモリを指すポインタを発見できない場合でも再利用してはならないものを、指定することができる:
void declare_reachable(void* p); // p で始まるメモリ領域 // (と、その大きさを知っている何らかの // アロケータ操作で割り付けられた領域) // は回収してはならない template<class T> T* undeclare_reachable(T* p); void declare_no_pointers(char* p, size_t n); // p[0..n] にはポインタなし void undeclare_no_pointers(char* p, size_t n);プログラマはポインタ安全性と回収についての規則が有効か調べることもできる:
enum class pointer_safety {relaxed, preferred, strict }; pointer_safety get_pointer_safety();もし、安全に導き出されていないポインタの値で参照を行ったり割り付けを破棄したりして、そのポインタが参照している完全なオブジェクトが動的記憶期間の範囲内にあり、それが前もって到達可能 (20.7.13.7) と宣言されていない場合、その動作は未定義だ。
以下も参照
カギとなる保証: 二つの実行スレッドは、異なるメモリの位置を、互いに干渉することなく更新したり参照したりできる。 だが、『異なるメモリの位置』とは何なのか? メモリの位置とは、スカラー型のオブジェクト、ないしはゼロより大きい幅を持つ全ての整列したビットフィールドの最大列のどちらかだ。 例えば、以下の S は正確に四つに別れたメモリの位置を持つ:
struct S { char a; // 位置 #1 int b:5, // 位置 #2 int c:11, int :0, // メモ: :0 は「特別な」位置 int d:8; // 位置 #3 struct {int ee:8;} e; // 位置 #4 };なぜこれが重要なのか? これは自明ではないのか? これが常に真でないことなどあり得るのか? 問題は、複数の演算が実際に動作する際に、複数の (明らかに) 無関係な命令同士が同時に動いてしまい、メモリハードウェアの気まぐれが表面化し得るということなのだ。 実際、コンパイラの支援がなければ、命令とデータパイプラインと細かいキャッシュ使用の問題が、完全に制御不能な形でアプリケーションプログラマに襲いかかるだろう。 二つのスレッドが共有データを持つと定義されていない場合ですら、そうなるのだ! 二つの別々にコンパイルされた『スレッド』を考えよ:
// スレッド1: char c; c = 1; int x = c; // スレッド2: char b; b = 1; int y = b;より現実的な議論をするために、コンパイラ/オプティマイザがメモリアクセスを切り詰めて c と b を単純に無視し、x と y を直接 1 で初期化することはない、と保証するため、別々に (各スレッドの範囲で) コンパイルされたものとする。 x と y の取り得る値は何になるだろう? C++11 に従えば、正しい答えは明白に一つだ: 1 と 1。 これが面白い理由は、従来の非並列な正しい C や C++ コンパイラでは、取り得る値が 0 と 0 (ありそうにないが)、1 と 0、0 と 1、1 と 1 のどれにもなり得ることだ。 これは『でたらめ』に見える。 どうしてか? リンカは c と b をそれぞれ隣接して (同じワードに) 配置するかも知れない -- C や C++ の 1990 年代の標準では何とも唱っていない。 これは即ち、C++ は、本物の並列ハードウェアのことを考慮して設計されていない全ての言語と同様、ということだ。 しかしながら、殆どの現代的なプロセッサは単一の文字だけを読み書きできず、ワード全体を読み書きする必要があるため、c への代入が、実際には、『c を含むワードを読み、c の部分を置き換え、ワードを書き直す』という動作になる。 b への代入も同様なので、二つのスレッドが (ソーステキスト上では) データを共有していなかったとしても、上書きしてしまう機会は大いにあるのだ!
そこで、C++11 では『別れたメモリの位置』の問題が発生しないように保証した。 より正確には: メモリの位置は、両方共リードアクセスされる限り、何らかのロックなしには二つのスレッドから安全にアクセスすることはできないということだ。 単一ワード中の異なるビットフィールドは別れたメモリ位置ではないので、何らかの形のロックなしにビットフィールドを持つ構造体をスレッド間で共有しないこと。 注意書きを別にすれば、C++ メモリモデルは、単純に『みんなが思っている通り』ということだ。
しかしながら、低水準の並列性問題については常に簡単に安直な考えができるわけではない。 以下を考えよ:
// x==0 かつ y==0 で始まる if (x) y = 1; // スレッド 1 if (y) x = 1; // スレッド 2
これに何か問題があるだろうか? より正確に言うと、データ競合があるだろうか? (答えはない)
ありがたいことに、我々は既に現代に適応済で、全ての並列 C++ コンパイラ (筆者の知る範囲で) は唯一の正しい答えを何年も前から提供している。 ほとんどの (残念ながらまだ全てではないが) トリッキーな質問に対しても同様だ。 結局のところ、C++ は『ずっと』並列システムの重要なシステムプログラミングに使われてきたのだ。 この標準でずっとより良くなるだろう。
以下も参照
平行性、並列性、スレッドについては、多くの分厚い本と何万もの論文が書かれているので、この FAQ の項目では単に大筋をなぞるに留める。 並列性についてきちんと考えるのは大変なのだ。 並列プログラミングについて理解を深めたいなら、最低限本を一冊読んで欲しい。 マニュアルや標準、FAQ だけに頼ってはいけない。
スレッドは std::thread を関数や関数オブジェクト (ラムダを含む) で構築することにより起動される:
#include<thread> void f(); struct F { void operator()(); }; int main() { std::thread t1{f}; // f() は別のスレッドで実行 std::thread t2{F()}; // F()() は別のスレッドで実行 }残念ながら、これはあまりまともな結果にならない -- f() と F() が何をしようが。 この思わぬ障害は、t1 が f() を実行する前か後、あるいは t2 が F() を実行する前か後に、このプログラムが終了するかも知れないことだ。 二つのタスクが完了するのを待ち合わせる必要があるのだ:
int main() { std::thread t1{f}; // f() は別のスレッドで実行 std::thread t2{F()}; // F()() は別のスレッドで実行 t1.join(); // t1 を待つ t2.join(); // t2 を待つ }join() で、スレッドが完了するまで停止しないことを保証する。 『join』は『スレッドが停止するまで待つ』ことを意味する。
実行すべきタスクに引数をいくつか渡したい (スレッドが実行する何かのタスクを呼び出す) こともある。 例えば:
void f(vector<double>&); struct F { vector<double>& v; F(vector<double>& vv) :v{vv} { } void operator()(); }; int main() { std::thread t1{std::bind(f,some_vec)}; // f(some_vec) は別のスレッドで実行 std::thread t2{F(some_vec)}; // F(some_vec)() は別のスレッドで実行 t1.join(); t2.join(); }基本的には、標準ライブラリ bind で引数付きの関数オブジェクトを作る。
一般的には、実行したタスクから戻ってきた結果を受け取りこともあるだろう。 素のタスクには戻り値の表記がないので、そのような用途には std::future をお勧めする。 あるいは、タスクへ結果を戻す引数を渡してそこへ格納するよう指示しても良い: 例えば:
void f(vector<double>&, double* res); // res に結果を格納 struct F { vector<double>& v; double* res; F(vector<double>& vv, double* p) :v{vv}, res{p} { } void operator()(); // res に結果を格納 }; int main() { double res1; double res2; std::thread t1{std::bind(f,some_vec,&res1)}; // f(some_vec,&res1) は別スレッドで実行 std::thread t2{F(some_vec,&res2)}; // F(some_vec,&res2)() は別スレッドで実行 t1.join(); t2.join(); std::cout << res1 << ' ' << res2 << '\n'; }だが、エラーはどうなるのだろう? もしタスクが例外を投げたらどうなるのか? タスクが例外を投げ、それを自分自身で捕捉しない場合、std::terminate() が呼び出される。 これは、一般的にはプログラムが終了することを意味する。 普通は何とかしてこれを避けたい。 std::future なら例外を親/呼出元スレッドに送出できる; 著者が future を好む理由の一つだ。 さもなくば、何らかの種類のエラーコードを返却すること。
thread がスコープから抜けた時は、そのタスクが完了していない限り、プログラムは terminate() する。 これは明らかに避けるべきだ。
thread に対して停止するよう要求 (可能な限り速やか、かつ段階を追って終了するよう要求するとか) したり、スレッドを強制停止させる (kill のような) 方法はない。 以下はオプションとなっている。
スレッドの基本的な問題はデータの競合だ; 単一のアドレス空間で実行中の二つのスレッドが独立してあるオブジェクトにアクセスできることで、未定義の結果が発生してしまうからだ。 片方 (あるいは両方とも) のスレッドがオブジェクトに書き込み、もう片方 (あるいは両方とも) がそのオブジェクトを読み出すと、その操作は『競合』することになる。 その結果は単なる未定義でなく、普通は完全に予測不能になるのだ。 そこで C++11 ではプログラマがデータ競合を回避できるよう、いくつかの規則/保証を提供している:
以下は可能だ。
以下も参照
std::mutex m; int sh; // 共有データ // ... m.lock(); // 共有データの操作: sh+=1; m.unlock();ただ一つのスレッドだけが、lock() と unlock() の間のコード (しばしばクリティカルリージョンと呼ばれる) を実行できる。 一つ目のスレッドがクリティカルリージョンを実行している時に、二つ目のスレッドが m.lock() しようとした場合、一つ目のスレッドが m.unlock() を実行するまで二つ目のスレッドはブロックされる。 これは単純だ。 単純でないのは、深刻な問題を引き起こさないように mutex を使う方法だ: スレッドが unlock() するのを『忘れた』ら? スレッドが同じ mutex を lock() しようとしたら? スレッドが unlock() するまでに非常に長い時間が掛かったら? スレッドが仕事をこなすのに二つの mutex を lock() する必要があったら? 完全な答えには本が何冊もいる。 ここでは (セクションのロックによる) 本当に簡単な内容に留める。
lock() に加えて、mutex には、ブロックされることなくクリティカルリージョンに進入可能か試行する try_lock() 操作がある:
std::mutex m; int sh; // 共有データ // ... if (m.try_lock()) { // 共有データの操作: sh+=1; m.unlock(); else { // 多分他のことをする }
recursive_mutex は、スレッドが複数回取得してよい mutex だ:
std::recursive_mutex m; int sh; // 共有データ // ... void f(int i) { // ... m.lock(); // 共有データの操作: sh+=1; if (--i>0) f(i); m.unlock(); // ... }ここでは f() 自身を乱暴に呼び出している。 通常、このコードは更に油断ならない。 f() を呼び出す行が、g() を呼び出し、それが h() を呼び出し、それが f() を呼び出す間接的な再起呼出になっているかも知れない。
mutex を続く十秒間確保する必要がある場合は? timed_mutex クラスがその役割を提供する。 これは try_lock() にタイムアウト制限が付いた特殊化版だ:
std::timed_mutex m; int sh; // 共有データ // ... if (m.try_lock_for(std::chrono::seconds(10))) { // 共有データの操作: sh+=1; m.unlock(); } else { // mutex を確保していないので、何か他のことをする }try_lock_for() は引数として duration を受け取り、その相対時間の間、待機する。 ある決まった時刻 time_point まで待機したい場合は、try_lock_until() を使えばよい:
std::timed_mutex m; int sh; // 共有データ // ... if (m.try_lock_until(midnight)) { // 共有データの操作: sh+=1; m.unlock(); } else { // mutex を確保していないので、何か他のことをする }midnight は出来の悪いジョークだ: mutex のような低水準の機構では、時間分解能は時間単位ではなくミリ秒単位になるだろう。
もちろん、recursive_timed_mutex もある。
mutex はリソース (実際のリソースを表すのに用いられるリソースと大体同じように) と考えられ、それが役に立つためには少なくとも二つのスレッドから見えなければならない。 従って、mutex をコピーしたり移動することはできない (ハードウェアの入力レジスタをコピーを作れないのと同じように)。
lock() と unlock() を合致させるのは驚くほど難しくなることもある。 複雑な制御構造や、エラーや、例外のことを考えてみて欲しい。 選択の余地があるのなら、mutex の操作に lock を使うこと; あなたは救われ、ユーザは長い待ちから救われるだろう。
以下も参照。
std::mutex m; int sh; // 共有データ // ... void f() { // ... std::unique_lock lck(m); // 共有データの操作: sh+=1; }lock を移動させることはできる (lock の目的は非局所リソースを局所的に表わすことだ) が、コピーはできない (どのコピーがリソース/mutex を所有するというのか?)。
この安直な lock の図式は、mutex にできることが全て可能で、より安全な unique_lock により雲行きが怪しくなる。 例えば、ロックの試行に lock を使える:
std::mutex m; int sh; // 共有データ // ... void f() { // ... std::unique_locklck(m,std::defer_lock); // lock を作るが、mutex は確保しない // ... if (lck.try_lock()) { // 共有データの操作: sh+=1; } else { // 何か他のことをする } }同様に、unique_lock は try_lock_for() や try_lock_until をサポートする。 lock を使わずに mutex を直接使うことで、例外の扱いや unlock() 忘れをせずにすむ。 並列プログラミングでは、得られる限りのあらゆる支援が必要なのだ。
二つのリソースを表すのに二つの mutex が必要な場合は? 安直な方法は順に mutex を取得することだ:
std::mutex m1; std::mutex m2; int sh1; // 共有データ int sh2 // ... void f() { // ... std::unique_lock lck1(m1); std::unique_lock lck2(m2); // 共有データの操作: sh1+=sh2; }このコードでは、何か他のスレッドが m1 と m2 の逆順の取得を試行できるため、先行する片方の lock の後、二つ目を永遠に待ち続けるという、潜在的に死んだフローがある (これをデッドロックと呼ぶ)。 システム中に多くのロックがある場合、これは本当に危険だ。 そこで、標準 lock は (安全に) 二つ以上の lock の取得の試行のため、二つの関数を提供している:
void f() { // ... std::unique_lock lck1(m1,std::defer_lock); // lock を作るが、mutex の獲得はまだ試行しない std::unique_lock lck2(m2,std::defer_lock); std::unique_lock lck3(m3,std::defer_lock); lock(lck1,lck2,lck3); // 共有データの操作 }明らかなことだが、lock() の実装にはデッドロックを避けるために細心の注意をもって当たる必要がある。 本質的に、これは try_lock() を注意深く使うのと同じことだ。 lock() が全てのロックを獲得できなかった場合、例外が送出される。 実際には、lock() は lock()、try_lock()、unlock() メンバ関数と何でも引数を取れるため (mutex のように)、どの例外を lock() が送出するか規定することができなかった。 引数に依存するからだ。
try_lock() の方を使用したいなら、lock() と同じものがあることが参考になる。
void f() { // ... std::unique_lock lck1(m1,std::defer_lock); // lock を作るが、mutex の獲得はまだ試行しない std::unique_lock lck2(m2,std::defer_lock); std::unique_lock lck3(m3,std::defer_lock); int x; if ((x = try_lock(lck1,lck2,lck3))==-1) { // C の世界にようこそ // 共有データの操作 } else { // x は取得できなかった mutex のインデックスを持つ // 例えば、lck2.try_lock() が失敗した場合は x==1 } }
以下も参照。
申し訳ないが、この項目を書く時間がない。 また後日確認されたい。
以下も参照
現在の time_point を知りたい場合、now() を三つのうち一つから呼び出すことができる: system_clock、monotonic_clock、high_resolution_clock だ。 例えば:
monotonic_clock::time_point t = monotonic_clock::now(); // 何かする monotonic_clock::duration d = monotonic_clock::now() - t; // 何かするのに d 時間単位かかったclock は time_point を返す。 duration は同じ clock から見た二つの time_point の差だ。 例によって、細かいことに拘らないなら、auto が友人になる。
auto t = monotonic_clock::now(); // 何かする auto d = monotonic_clock::now() - t; // 何かするのに d 時間単位かかった
この時間機能はシステムの深淵な利用を効果的にサポートすることを意図したもので、スケジュール帳をメンテナンスするのに便利な機能を提供するわけではないのだ。 実際、時間機能は高エネルギー物理の切迫したニーズに由来する。 全ての時間スケール (世紀とかピコ秒とか) を表現できるようにしたり、単位や誤記、丸め誤差に関する混乱を避けるため、duration と time_point はコンパイル時有理数パッケージを用いて表現される。 duration には二つの部分がある: クロック数『tick』と tick が何を意味するか (秒とかミリ秒とか) を表すもの (『period』) で、period の部分は durations 型になっている。 この表は、標準ヘッダ <ratio> からのもので、SI 単位系 (MKS 単位系やメートル法としても知られる) の時間を定義したものだが、利用のスコープに関する考えが分かるだろう:
// SI 単位系コンビニエンス typedef: typedef ratio<1, 1000000000000000000000000> yocto; // 条件付サポート typedef ratio<1, 1000000000000000000000> zepto; // 条件付サポート typedef ratio<1, 1000000000000000000> atto; typedef ratio<1, 1000000000000000> femto; typedef ratio<1, 1000000000000> pico; typedef ratio<1, 1000000000> nano; typedef ratio<1, 1000000> micro; typedef ratio<1, 1000> milli; typedef ratio<1, 100> centi; typedef ratio<1, 10> deci; typedef ratio< 10, 1> deca; typedef ratio< 100, 1> hecto; typedef ratio< 1000, 1> kilo; typedef ratio< 1000000, 1> mega; typedef ratio< 1000000000, 1> giga; typedef ratio< 1000000000000, 1> tera; typedef ratio< 1000000000000000, 1> peta; typedef ratio< 1000000000000000000, 1> exa; typedef ratio< 1000000000000000000000, 1> zetta; // 条件付サポート typedef ratio<1000000000000000000000000, 1> yotta; // 条件付サポートコンパイル時有理数は、有効な duration と time_point の全ての組み合わせについて、通常の四則演算子 (+、-、*、/) と比較演算子 (==、!=、<、<=、>、>=) を提供する (例えば、time_point 同士を加算することはできない)。 これらの演算子はオーバーフローやゼロ除算のチェックも行う。 これはコンパイル時の機能なので、実行時の性能を気にする必要はない。 加えて、duration については ++、--、+=、-=、*=、/= を使用できるし、time_point tp と duration d については tp+=d や tp-=d とできる。
ここで、<chrono> で定義されている、標準の duration 型を使った値の例をいくつか挙げる:
microseconds mms = 12345; milliseconds ms = 123; seconds s = 10; minutes m = 30; hours h = 34; auto x = std::chrono::hours(3); // namespace を明示 auto x = hours(2)+minutes(35)+seconds(9); // 適当な「using」があると仮定duration を小数で初期化することはできない。 例えば、2.5 秒ではなく、2500 ミリ秒を使うこと。 これは、duration を『tick』の数値に読み替えているためだ。 各々の tick は duration の『period』単位 (上述した milli や kilo のような) で表される。 既定の単位は seconds なので、period が 1 で tick が 1 の duration は一秒に当たる。 duration を明示的に表現することもできる:
duration<long> d0 = 5; // 秒 (既定) duration<long,kilo> d1 = 99; // キロ秒! duration<long,ratio<1000,1>> d2 = 100; // d1 と d2 は同じ型 ("kilo"は"1000 倍"を意味する)実際に duration で、例えば出力のようなことを行う場合は、分 とか マイクロ秒 といった単位を与えてやる必要がある。 例えば:
auto t = monotonic_clock::now(); // 何かする nanoseconds d = monotonic_clock::now() - t; // 計算結果はナノ秒で欲しい cout << "何かするのに " << d << " ナノ秒かかった\n";あるいは、duration を浮動小数に変換して (誤差を丸めて) もよい。
auto t = monotonic_clock::now(); // 何かする auto d = monotonic_clock::now() - t; cout << "何かするのに " << duration_cast<double>(d).count() << " 秒かかった\n";count() は『tick』の数だ。
以下も参照
以下も参照
標準には三種類の future があって、future がもっとも単純な用途向け、shared_future と atomic_future はいくらかトリッキーな用途のためのものだ。 future がもっとも単純で、著者に必要なことが全て可能であるため、ここでは future を示すに留める。 future<X> が f を呼び出している場合、そこから X 型の値を get() することができる:
X v = f.get(); // 必要なら得られる値が算出されるまで待機値がまだ存在しない場合、到着するまでこのスレッドはブロックされる。 値が算出できなかった場合、get() は例外を送出するかも知れない (システムまたは値を get() しようとしたタスクから送出)。
結果を待ちたくないこともあるかも知れないので、結果が到着済か future に問い合わせることもできる:
if (f.wait_for(0)) { // get() すべき値が存在する // 何かする } else { // 何か他のことをする }しかしながら、future の主目的は get() を単純にすることだ。
promise の主目的は、future の get() と対になる『put』(珍しいが『set』とも呼ばれる) を単純にすることだ。 『future』と『promise』という名前は歴史的な理由によるものなので、文句を言わないで欲しい。 ダジャレの豊かな源でもあることだし。
promise から X 型の結果を future に送る (戻す) 必要がある場合、基本的にできることは二つある: 値を渡すか例外を渡すかだ。
try { X res; // res の値を計算 p.set_value(res); } catch (...) { // おっと: res を計算できなかった p.set_exception(std::current_exception()); }
ここまでは良いとして、どのようにして、こちらの thread とあちらの thread とで、future/promise のペアを合致させるのか? ええと、future と promise は移動できる (コピーはできない) ので、広範な可能性がある。 明らかに分かることは、結果を配置するための場所として繋がっている future が保持されている間は、スレッドを作成して、promise を与えるタスクが誰にとっても必要だろうということだ (訳文推敲中: The most obvious idea is for whoever wants a task done to create a thread and give the promise to it while keeping the corresponding future as the place for the result.)。 async() を利用することが、このテクニックのもっとも極端/エレガントな別解だ。
packaged_task 型は単純にタスクを実行するスレッドを起動するために提供されている。 特に、promise に繋がっている future のセットアップを取り扱い、タスクから promise に戻り値や例外を置くためのラッパーコードを提供する。 例えば:
double comp(vector<double>& v) { // タスクをパッケージ化: // (このタスクは double の配列に対して標準 accumulate() すること): packaged_task<double(double*,double*,double)> pt0{std::accumulate<double*,double*,double>}; packaged_task<double(double*,double*,double)> pt1{std::accumulate<double*,double*,double>}; auto f0 = pt0.get_future(); // future の在処を取得 auto f1 = pt1.get_future(); pt0(&v[0],&v[v.size()/2],0); // スレッドを開始 pt1(&v[v.size()/2],&v[size()],0); return f0.get()+f1.get(); // 結果を取得 }
以下も参照
ここに挙げる例は、並列プログラミングのごたごたしたスレッド + ロックをプログラマに思いとどまらせる方法だ:
template<class T, class V> struct Accum { // 単純な累算関数オブジェクト T* b; T* e; V val; Accum(T* bb, T* ee, const V& v) : b{bb}, e{ee}, val{vv} {} V operator() () { return std::accumulate(b,e,val); } }; double comp(vector<double>& v) // v が十分大きい場合、多くの子タスクが起動 { if (v.size()<10000) return std::accumulate(v.begin(),v.end(),0.0); auto f0 {async(Accum{&v[0],&v[v.size()/4],0.0})}; auto f1 {async(Accum{&v[v.size()/4],&v[v.size()/2],0.0})}; auto f2 {async(Accum{&v[v.size()/2],&v[v.size()*3/4],0.0})}; auto f3 {async(Accum{&v[v.size()*3/4],&v[v.size()],0.0})}; return f0.get()+f1.get()+f2.get()+f3.get(); }これは非常に純朴な並列プログラミングの利用だが (『マジックナンバー』に注意)、明示的に thread、lock、バッファ等がないことに注意されたい。 f-変数の型は、future となる標準ライブラリ関数 async() の戻り値型によって決まる。 必要なら、future の get() が thread の完了まで待機する。 ここで、async() の仕事は必要な子 thread を起動することであり、future の仕事は適当な thread を join() することだ。 『単純』であることは async()/future の設計においてもっとも重要な観点だ; future は一般的に thread として使うこともできるが、async() を I/O や mutex の操作やその他の方法で別のタスクと相互作用するタスクの起動に使おうと考えることすらしてはいけない。 async() の背景にある考えは、範囲-for 文の背景にある考えと同じだ: もっとも単純な例を扱う単純な方法を提供し、更に複雑な例は完全に一般的な仕組みにお任せすることだ。
async() は、新しい thread や、呼出元以外のあらゆる thread、あるいは async() がそれを良い考えだと『思う』場合に限り、異なる thread でも起動要求されることがある。 後者は、ユーザの視点からはもっとも単純であり、潜在的にはもっとも効率的だ (単純なタスクに関する限り)。
以下も参照
uniform_int_distribution<int> one_to_six {1,6}; // 分布は 1..6 の int に割り当てられる default_random_engine re {}; // 既定のエンジン乱数を取得するには、エンジン付きで分散を呼び出せばよい:
int x = one_to_six(re); // x は [1:6] の範囲になる呼出毎に毎回エンジンを渡すのは飽き飽きするだろうから、引数なしで呼び出せる関数オブジェクトを得るための引数を bind できる:
auto dice {bind(one_to_six,re)}; // 乱数生成器の作成 int x = dice(); // サイコロを振る: x は [1:6] の範囲になる一般性と効率性に対する妥協のないこだわりのおかげで、標準ライブラリの乱数コンポーネントは『全ての乱数ライブラリが収まるまでに巨大化した』と、ある専門家に評されるようになった。 しかしながら、このことは『初心者に優しく』ないと指弾されるかも知れない。 著者は乱数インターフェースが性能上のボトルネックになったことなど全く見たことがないのだが、非常に単純な乱数生成器を必要としない初心者 (どんな背景を持つにせよ) を教えたことはない。 以下のようなもので十分だろう
int rand_int(int low, int high); // [low:high]の範囲で一様に分布する乱数を生成それでは、その値をどのようにして得るのか? rand_int() の中で dice() のような何かを得る必要がある:
int rand_int(int low, int high) { static default_random_engine re {}; using Dist = uniform_int_distribution<int>; static Dist uid {}; return uid(re, Dist::param_type{low,high}); }この定義はまだ『上級者レベル』だが rand_int() の使用だけなら C++ コースの最初の一週間で扱える。
自明でない例を見せるためだけに、ここに正規分布を生成して出力するプログラムを用意した:
default_random_engine re; // 既定のエンジン normal_distribution<int> nd(31 /* 平均 */,8 /* 標準偏差 */); auto norm = std::bind(nd, re); vector<int> mn(64); int main() { for (int i = 0; i<1200; ++i) ++mn[round(norm())]; // 生成 for (int i = 0; i< mn.size(); ++i) { cout << i << '\t'; for (int j=0; j<mn[i]; ++j) cout << '*'; cout << '\n'; } }結果は以下の通りだ:
0 1 2 3 4 * 5 6 7 8 9 * 10 *** 11 *** 12 *** 13 ***** 14 ******* 15 **** 16 ********** 17 *********** 18 **************** 19 ******************* 20 ******************* 21 ************************** 22 ********************************** 23 ********************************************** 24 ******************************************** 25 ***************************************** 26 ********************************************* 27 ********************************************************* 28 *************************************************** 29 ****************************************************************** 30 ********************************************** 31 ********************************************************************* 32 ********************************************** 33 ************************************************************* 34 ************************************************************** 35 *************************************** 36 *********************************************** 37 ********************************************** 38 ********************************************* 39 ******************************** 40 ******************************************** 41 *********************** 42 ************************** 43 ****************************** 44 ***************** 45 ************* 46 ********* 47 ******** 48 ***** 49 ***** 50 **** 51 *** 52 *** 53 ** 54 * 55 * 56 57 * 58 59 60 61 62 63
以下も参照
以下も参照
「concept」は、型や、型同士の組み合わせ、型と整数の組み合わせに必要であることを表現するための仕掛けだ。 これは特に、template の使用の早期チェックを行うのに便利だ。 また逆に、template 本体の早期エラー検出の助けにもなる。 標準ライブラリのアルゴリズム fill を考えよ:
template<ForwardIterator Iter, class V> // 型の型 requires Assignable<Iter::value_type,V> // 引数型同士の関係 void fill(Iter first, Iter last, const V& v); // 宣言のみ、定義なし fill(0, 9, 9.9); // Iter は int なので、エラー: int は ForwardIterator でなく // int には前置 * がない fill(&v[0], &v[9], 9.9); // Iter は int*、ok: int* は ForwardIteratorfill() は宣言しただけで、定義 (実装) していないことに注意されたい。 一方で、fill() が引数から必要とするものは明示的に言明されている:
template の実装者も concept の恩恵を受ける。 以下を考えよ:
template<ForwardIterator Iter, class V> requires Assignable<Iter::value_type,V> void fill(Iter first, Iter last, const V& v) { while (first!=last) { *first = v; first=first+1; // エラー: + は ForwardIterator では定義されない // (++first を使用) } }このエラーは直ちに捉えられ、大量の退屈なテストが不要になる (もちろん全部のテストではないが)。
異なる型の型を分類し、区別できるようになったことで、渡すべき型の種類に基づいたオーバーロードが可能になる。 例えば
// イテレータベースの標準ソート (concept 付き): template<Random_access_iterator Iter> requires Comparable<Iter::value_type> void sort(Iter first, Iter last); // 通常の実装を使用 // コンテナベースのソート: template<Container Cont> requires Comparable<Cont::value_type> void sort(Cont& c) { sort(c.begin(),c.end()); // イテレータ版の単純な呼び出し } void f(vector<int>& v) { sort(v.begin(), v.end()); // ある方法 sort(v); // もう一つの方法 // ... }
自分用の concept を定義することも可能だが、初心者のために ForwardIterator、Callable、LessThanComparable、Regular といった様々な便利な concept を標準ライブラリが提供している。
メモ: C++0x 標準ライブラリは concept を用いて規定されている。
以下も参照
int* は ForwardIterator である。 concept を説明する時に著者達はそう言ったし、標準ライブラリも常にそう言ってきたし、イテレータにポインタを使っていた最初のバージョンの STL ですらそうだった。 しかしながら、ForwardIterator の value_type についても話してきたが、int* には value_type メンバがないし、そもそもメンバは一つもない。 では、int* はどうして ForwardIterator 足り得るのか? 著者達がそう言っているから、というのがその理由だ。 concept_map を使うことで、ForwardIterator が要求される場所で T* を使えるかを明示できる。 T とその value_type を考えよう:
template<Value_type T> concept_map ForwardIterator<T*> { // T* の value_type は T typedef T value_type; };concept_map があれば、どのようにして型を参照したいかを示すことができ、修正したり新しい型にラップする必要もない。 「concept map」は、汎用用途を目的として個々に開発されたソフトウェアをつなぐ、非常に柔軟で一般性のある仕組みなのだ。
axiom は等価と考えられる演算のペアを列挙したものだ。 以下を考えよ:
concept Semigroup<typename Op, typename T> : CopyConstructible<T> { T operator()(Op, T, T); axiom Associativity(Op op, T x, T y, T z) { op(x, op(y, z)) <=> op(op(x, y), z); // T の演算子は結合的 (associative) であると仮定 } } concept Monoid<typename Op, typename T> : Semigroup<Op, T> { // モノイド (monoid) は単位元 (identity element) を持つ半群 (semigroup) T identity_element(Op); axiom Identity(Op op, T x) { op(x, identity_element(op)) <=> x; op(identity_element(op), x) <=> x; } }この <=> は等価演算子であり、axiom でだけ用いる。 axiom の証明は (一般に) できないことに注意すること。 証明できないことを記述するために axiom を使うからだが、プログラマが記述できるのは許容され得る仮定になる。 等価文の両辺は値によっては非合法になり得ることに注意すること。 例えば、浮動小数点数における NaN (非数値) がそうだ: もし等価文の両辺が共に NaN の場合は (明らかに) 両辺とも無効かつ等価になる (axiom の記述とは無関係に) が、片方だけが NaN の場合は axiom の利点が生きるかも知れない。
axiom は等価文 (<=> を用いた) と条件文 (「if (何とか) 以降の等価を仮定」の形式) の羅列だ:
// concept TotalOrder 中で: axiom Transitivity(Op op, T x, T y, T z) { if (op(x, y) && op(y, z)) op(x, z) <=> true; // 条件付きで等価 }以下も参照