C++11は、国際標準化機構(ISO)と国際電気標準会議(IEC)によるC++プログラミング言語の共同技術標準ISO/IEC 14882のバージョンです。C++11は、 C++03という以前のバージョンのC++標準に代わるものであり、[1]後にC++14に置き換えられました。この名前は、仕様の公開年で言語バージョンに名前を付けるという伝統に従っていますが、 2010年より前に公開される予定だったため、以前はC++0xという名前でした。[2]
設計目標の1つは、コア言語の変更よりもライブラリの変更を優先することでしたが、[3] C++11ではコア言語にいくつかの追加が行われています。コア言語で大幅に改善された領域には、マルチスレッドのサポート、ジェネリックプログラミングのサポート、統一された初期化、パフォーマンスなどがあります。C ++標準ライブラリにも大幅な変更が加えられ、数学特殊関数のライブラリを除くC++テクニカルレポート1(TR1)ライブラリのほとんどが組み込まれました。[4]
C++11は2011年9月にISO/IEC 14882:2011 [5]として発行され、有料で入手可能です。公開されたC++11標準に最も近いワーキングドラフトは2012年1月16日のN3337です。[6] C++11標準からの編集上の修正のみが含まれています。[7]
C++11はClang 3.3以降で完全にサポートされています。[8] C++11はGNUコンパイラコレクション(GCC)4.8.1以降で完全にサポートされています。[9]
設計目標
設計委員会は、C++11 の設計においていくつかの目標を貫こうとしました。
- 古いコードとの安定性と互換性を維持する
- コア言語を拡張するのではなく、標準ライブラリを介して新しい機能を導入することを好む
- 特定のアプリケーションにのみ役立つ新機能を導入するのではなく、システムとライブラリの設計を容易にするために C++ を改良する
- 以前の安全でない手法に代わるより安全な代替手段を提供することで、型の安全性を高める
- パフォーマンスとハードウェアを直接操作する能力を向上
- 現実世界の問題に適切な解決策を提供する
- 熟練したプログラマーに必要なユーティリティを削除せずに、C++ を教えやすく、学習しやすくする
初心者への配慮は重要であると考えられています。なぜなら、ほとんどのコンピュータプログラマーは常に初心者であり、多くの初心者は自分の知識を広げることなく、専門とする言語の側面での作業に限定してしまうからです。[2] [3]
C++ コア言語の拡張
C++ 委員会の機能の 1 つは、言語コアの開発です。コア言語で大幅に改善された領域には、マルチスレッドサポート、汎用プログラミングサポート、統一された初期化、パフォーマンスなどがあります。
コア言語ランタイムパフォーマンスの強化
これらの言語機能は、主にメモリまたは計算速度のいずれかの実行時パフォーマンス上の利点を提供するために存在します。 [引用が必要]
右辺値参照と移動コンストラクタ
C++03(およびそれ以前)では、一時変数(代入の右側にあることが多いため「右辺値const T&」と呼ばれる)は、Cと同様に変更できないように意図されており、型と区別できないと考えられていました。しかし、場合によっては、一時変数が変更される可能性があり、この動作は便利な抜け穴であるとさえ考えられていました。[10] C++11では、新しい非const参照型が追加されました。右辺値参照、 によって識別されますT&&。これは、「移動セマンティクス」を可能にする目的で、初期化後に変更が許可されている一時オブジェクトを指します。
C++03 の慢性的なパフォーマンスの問題は、オブジェクトが値渡しされるときに暗黙的に発生する可能性のある、コストのかかる不要なディープ コピーstd::vector<T>です。この問題を説明するために、 が内部的には、定義されたサイズを持つ C スタイルの配列のラッパーであると考えてください。std::vector<T>一時オブジェクトが関数から作成されるか返される場合、新しい を作成し、そこにすべての右辺値のデータをコピーすることによってのみ、一時オブジェクトを保存できますstd::vector<T>。その後、一時オブジェクトとそのすべてのメモリが破棄されます。(簡単にするために、この説明では戻り値の最適化については考慮しません。)
std::vector<T>C++11 では、への右辺値参照を受け取るの移動コンストラクタはstd::vector<T>、内部の C スタイル配列へのポインタを右辺値から新しい にコピーしstd::vector<T>、右辺値内のポインタを null に設定できます。一時オブジェクトは二度と使用されないため、どのコードも null ポインタにアクセスしようとしません。また、ポインタが null であるため、スコープ外になったときにそのメモリは削除されません。したがって、この操作はディープ コピーのコストを回避できるだけでなく、安全で目に見えないものになります。
右辺値参照は、標準ライブラリの外部で変更を加えることなく、既存のコードにパフォーマンス上の利点をもたらすことができます。一時値を返す関数の戻り値の型は、一時値は自動的に右辺値とみなされるため、移動コンストラクタを呼び出すためにstd::vector<T>明示的に に変更する必要はありません。(ただし、が移動コンストラクタのない C++03 バージョンである場合、コピー コンストラクタは で呼び出され、大量のメモリ割り当てが発生します。)
std::vector<T> &&std::vector<T>const std::vector<T>&
安全上の理由から、いくつかの制限が課せられています。名前付き変数は、たとえ右辺値として宣言されていても、右辺値としてみなされることはありませんstd::move()。右辺値を取得するには、関数テンプレートを使用する必要があります。右辺値参照は、主に移動コンストラクターで使用することを意図しており、特定の状況下でのみ変更できます。
rvalue 参照の文言の性質と、lvalue 参照 (通常の参照) の文言へのいくつかの変更により、rvalue 参照では開発者が完全な関数転送を提供できます。この機能は、可変長テンプレートと組み合わせると、特定の引数を取る別の関数に引数を完全に転送できる関数テンプレートを可能にします。これは、コンストラクター パラメーターを転送して、特定の引数に対して適切なコンストラクターを自動的に呼び出すファクトリー関数を作成する場合に最も役立ちます。これは、C++ 標準ライブラリ メソッドの emplace_back セットで確認できます。
constexpr – 一般化された定数式
C++ には、常に定数式の概念がありました。定数式とは3+4、コンパイル時と実行時に常に同じ結果をもたらす式です。定数式はコンパイラーにとって最適化の機会であり、コンパイラーは頻繁にコンパイル時に定数式を実行し、結果をプログラムにハードコードします。また、C++ 仕様では、いくつかの場所で定数式の使用が求められています。配列の定義には定数式が必要であり、列挙子の値は定数式でなければなりません。
ただし、定数式に関数呼び出しやオブジェクト コンストラクターを含めることは許可されていません。そのため、次のような単純なコードは無効です。
int get_five () { 5を返す;}
int some_value [ get_five () + 7 ]; // 12個の整数の配列を作成します。不正な形式のC++
get_five() + 7これは定数式ではないため、C++03 では無効でした。C++03 コンパイラにはget_five()、実行時に実際に定数であるかどうかを知る方法がありません。理論上、この関数はグローバル変数に影響を与えたり、他の非実行時定数関数を呼び出したりすることができます。
C++11ではキーワードが導入されconstexpr、関数またはオブジェクトのコンストラクタがコンパイル時定数であることをユーザーが保証できるようになりました。[11]上記の例は次のように書き直すことができます。
constexpr int get_five () { 5を返します;}
int some_value [ get_five () + 7 ]; // 12個の整数の配列を作成します。有効なC++11
get_five()これにより、コンパイラはそれがコンパイル時の定数である
ことを理解し、検証できるようになります。
関数で を使用するとconstexpr、その関数で実行できることにいくつかの制限が課せられます。まず、関数の戻り値の型は void 以外である必要があります。次に、関数本体では変数を宣言したり、新しい型を定義したりすることはできません。3 つ目に、本体には宣言、null ステートメント、および単一の return ステートメントのみを含めることができます。引数の置換後に、return ステートメント内の式が定数式を生成するような引数値が存在する必要があります。
C++11 より前では、変数の値は、変数が const として宣言され、定数式の初期化子を持ち、整数型または列挙型である場合にのみ、定数式で使用できました。C++11 では、次のconstexprキーワードで定義されている場合、変数は整数型または列挙型でなければならないという制限がなくなりました。
constexpr double earth_gravitational_acceleration = 9.8 ; constexpr double moon_gravitational_acceleration = earth_gravitational_acceleration / 6.0 ;
このようなデータ変数は暗黙的に const であり、定数式である初期化子を持つ必要があります。
ユーザー定義型から定数式データ値を構築するには、を使用してコンストラクタを宣言することもできますconstexpr。constexprコンストラクタの関数本体には宣言とヌル ステートメントのみを含めることができ、constexpr関数の場合のように変数を宣言したり型を定義したりすることはできません。引数の置換後に定数式を使用してクラスのメンバーを初期化するような引数値が存在する必要があります。このような型のデストラクタは単純である必要があります。
コンストラクターを持つ型のコピー コンストラクターは、constexpr通常、コンストラクターとしても定義されconstexpr、その型のオブジェクトが constexpr 関数から値として返されるようにする必要があります。コピー コンストラクター、演算子オーバーロードなど、クラスの任意のメンバー関数は、constexprconstexpr 関数の要件を満たしている限り、 として宣言できます。これにより、コンパイラはコンパイル時にオブジェクトをコピーし、それらに対して操作を実行できます。
constexpr 関数またはコンストラクタが定数式ではない引数で呼び出された場合、呼び出しは関数が constexpr ではないかのように動作し、結果の値は定数式ではありません。同様に、constexpr 関数の return ステートメント内の式が、特定の呼び出しに対して定数式に評価されない場合、結果は定数式ではありません。
constexprは、 C++20constevalで導入されたとは異なり、後者は常にコンパイル時定数を生成する必要があるのに対し、 にはこの制限がありません。
constexpr
プレーンオールドデータの定義の変更
C++03 では、クラスまたは構造体がPOD ( Plain Old Data ) 型と見なされるためには、いくつかのルールに従う必要があります。この定義に適合する型は、C と互換性のあるオブジェクト レイアウトを生成し、静的に初期化することもできます。C++03 標準では、コンパイラがプログラムを受け入れられない技術的な理由がなくても、C と互換性のある型や静的に初期化できる型に制限があります。誰かが C++03 POD 型を作成し、非仮想メンバー関数を追加した場合、この型は POD 型ではなくなり、静的に初期化できなくなり、メモリ レイアウトが変更されていなくても C と互換性がなくなります。
C++11 では、POD の概念をtrivialとstandard-layout という2 つの別個の概念に分割することで、いくつかの POD ルールが緩和されました。
単純な型は静的に初期化できます。またmemcpy、コピー コンストラクターを使用するのではなく、を介してデータをコピーすることも有効です。単純な型の有効期間は、コンストラクターが完了したときではなく、そのストレージが定義されたときに始まります。
単純なクラスまたは構造体は次のように定義されます。
- 単純なデフォルト コンストラクターがあります。デフォルト コンストラクター構文 (
SomeConstructor() = default;) を使用できます。 - デフォルトの構文を使用できる、単純なコピーおよび移動コンストラクターがあります。
- デフォルトの構文を使用できる、単純なコピーおよび移動代入演算子があります。
- 単純なデストラクタを持ちますが、これは仮想であってはなりません。
コンストラクターは、クラスの仮想メンバー関数と仮想基本クラスが存在しない場合にのみ単純です。コピー/移動操作では、すべての非静的データ メンバーが単純である必要があります。
標準レイアウトの型とは、C と互換性のある方法でメンバーを順序付けてパックすることを意味します。クラスまたは構造体は、定義により、次の場合に標準レイアウトになります。
- 仮想関数はありません
- 仮想基底クラスはありません
- すべての非静的データ メンバーは同じアクセス制御 (public、private、protected) を持ちます。
- 基本クラス内の非静的データメンバーを含むすべての非静的データメンバーは、階層内の同じクラスにあります。
- 上記のルールは、すべての基本クラスとクラス階層内のすべての非静的データメンバーにも適用されます。
- 最初に定義された非静的データメンバーと同じ型の基本クラスがない
クラス/構造体/共用体は、単純で標準レイアウトであり、そのすべての非静的データ メンバーと基本クラスが POD である場合に POD と見なされます。
これらの概念を分離することで、一方を失うことなく他方を放棄することが可能になります。複雑な移動およびコピー コンストラクターを持つクラスは単純ではないかもしれませんが、標準レイアウトであるため C と相互運用できます。同様に、パブリックおよびプライベートの非静的データ メンバーを持つクラスは標準レイアウトではありませんが、単純であるためmemcpy- 可能です。
コア言語ビルド時のパフォーマンス強化
外部テンプレート
C++03 では、翻訳単位で完全に指定されたテンプレートに遭遇するたびに、コンパイラはテンプレートをインスタンス化する必要があります。多くの翻訳単位で同じ型を使用してテンプレートがインスタンス化されると、コンパイル時間が大幅に長くなる可能性があります。C++03 ではこれを防ぐ方法がないため、C++11 では extern データ宣言に類似した extern テンプレート宣言が導入されました。
C++03 には、コンパイラにテンプレートのインスタンス化を義務付ける次の構文があります。
テンプレートクラスstd :: vector < MyClass > ;
C++11 では次の構文が提供されるようになりました。
外部テンプレートクラスstd :: vector < MyClass > ;
これは、この翻訳単位内のテンプレートをインスタンス化し ないようにコンパイラに指示します。
コア言語の使いやすさの向上
これらの機能は、言語を使いやすくすることを主な目的として存在します。これらにより、型の安全性が向上し、コードの繰り返しが最小限に抑えられ、エラーのあるコードの可能性が低くなることなどが実現します。
初期化リスト
C++03 は C から初期化リスト機能を継承しました。構造体または配列には、構造体のメンバーの定義順に、中括弧で囲まれた引数のリストが与えられます。これらの初期化リストは再帰的であるため、構造体の配列または他の構造体を含む構造体で使用できます。
構造体オブジェクト{ float first ; int second ; };
Object scalar = { 0.43f , 10 }; // 1 つのオブジェクト、first=0.43f、second=10 Object anArray [] = {{ 13.4f , 3 }, { 43.28f , 29 }, { 5.934f , 17 }}; // 3 つのオブジェクトの配列
これは、静的リストや、構造体を何らかの値に初期化する場合に非常に便利です。C++ では、オブジェクトを初期化するためのコンストラクターも提供されていますが、初期化リストほど便利ではないことがよくあります。ただし、C++03 では、Plain Old Data (POD) 定義に準拠する構造体とクラスでのみ初期化リストが許可されています。C++11 では初期化リストが拡張されているため、などの標準コンテナーを含むすべてのクラスで使用できますstd::vector。
C++11 では、この概念が と呼ばれるテンプレートにバインドされますstd::initializer_list。これにより、コンストラクターやその他の関数が初期化リストをパラメーターとして受け取ることができます。例:
クラスSequenceClass { public : SequenceClass ( std :: initializer_list < int > list ); };
これによりSequenceClass、次のような整数のシーケンスから構築できるようになります。
シーケンスクラスsome_var = { 1 , 4 , 5 , 6 };
このコンストラクタは、初期化リストコンストラクタと呼ばれる特殊なコンストラクタです。このようなコンストラクタを持つクラスは、統一初期化中に特別に扱われます(以下を参照)。
テンプレート クラスは、ファーストクラスの C++11 標準ライブラリ型std::initializer_list<>です。これらは、中括弧が と推論されるコンテキストで型名なしの構文を使用することによって、または のように型を明示的に指定することによって(その他のさまざまな構築構文についても同様)、C++11 コンパイラによって静的に構築できます。
{}std::initializer_liststd::initializer_list<SomeType>{args}
リストは一度作成すればコピーできます。これはコストがかからず、参照によるコピーとして機能します (クラスは通常、開始/終了ポインターのペアとして実装されます)。An はstd::initializer_list定数です。作成後はメンバーを変更できず、メンバー内のデータも変更できません (メンバーからの移動や、クラス メンバーへのコピーの要求などができなくなります)。
はコンパイラによって特別に構築されますが、std::initializer_list実際の型であるため、クラス コンストラクター以外の場所でも使用できます。通常の関数は、型指定されたstd::initializer_listを引数として受け取ることができます。例:
void function_name ( std :: initializer_list < float > list ); // コピーは簡単です。上記を参照してください
関数名({ 1.0f , -3.45f , -0.4f });
標準ライブラリでのこの例には、数値型の を
取るstd::min()およびテンプレートが含まれます。std::max()std::initializer_list
標準コンテナは次の方法でも初期化できます。
std :: vector < std :: string > v = { "xyzzy" , "plugh" , "abracadabra" }; std :: vector < std :: string > v ({ "xyzzy" , "plugh" , "abracadabra" }); std :: vector < std :: string > v { "xyzzy" , "plugh" , "abracadabra" }; // 下記の「Uniform 初期化」を参照
均一な初期化
C++03 では、型の初期化に関して多くの問題があります。初期化にはいくつかの方法があり、いくつかは入れ替えると異なる結果になります。たとえば、従来のコンストラクタ構文は関数宣言のように見える場合があり、コンパイラの最も厄介な解析ルールがそれを関数宣言と間違えないようにするための手順を実行する必要があります。集約初期化子 ( を使用) を使用して初期化できるのは、集約と POD 型だけですSomeType var = {/*stuff*/};。
C++11 では、あらゆるオブジェクトで機能する完全に統一された型の初期化を可能にする構文が提供されています。これは、初期化リスト構文を拡張したものです。
構造体BasicStruct { int x ; double y ; };
構造体AltStruct { AltStruct ( int x , double y ) : x_ { x } , y_ { y } {}
プライベート:
int x_ ; double y_ ; };
基本構造体var1 { 5 、3.2 };代替構造体var2 { 2 、4.3 };
の初期化は、var1集合初期化とまったく同じように動作します。つまり、オブジェクトの各データ メンバーは、初期化リストの対応する値を使用してコピー初期化されます。暗黙的な型変換は、必要な場所で使用されます。変換が存在しない場合、または縮小変換のみが存在する場合、プログラムは不適切です。 の初期化は、var2コンストラクタを呼び出します。
次のようにすることもできます:
構造体IdString { std :: string名前; int識別子; };
IdString get_string () { return { "foo" , 42 }; //明示的な型がないことに注意してください。}
統一された初期化は、コンストラクタ構文に代わるものではありません。コンストラクタ構文は、依然として必要な場合がTypeName(initializer_list<SomeType>);あります。クラスに初期化リスト コンストラクタ ( ) がある場合、初期化リストがシーケンス コンストラクタの型に準拠していれば、他の形式の構築よりも優先されます。 の C++11 バージョンには、std::vectorテンプレート型用の初期化リスト コンストラクタがあります。したがって、次のコードは次のようになります。
std ::ベクトル<int> the_vec {4} ;
は、単一のサイズ パラメータを受け取り、そのサイズのベクトルを作成する のコンストラクタではなく、初期化リスト コンストラクタを呼び出しますstd::vector。後者のコンストラクタにアクセスするには、ユーザーは標準のコンストラクタ構文を直接使用する必要があります。
型推論
C++03 (および C) では、変数を使用するには、その型を明示的に指定する必要があります。ただし、テンプレート型とテンプレート メタプログラミング手法の登場により、何かの型、特に関数の明確に定義された戻り値は、簡単に表現できない場合があります。したがって、中間値を変数に格納することは難しく、特定のメタプログラミング ライブラリの内部に関する知識が必要になる可能性があります。
C++11では、この問題を2つの方法で緩和できます。まず、明示的な初期化を伴う変数の定義でautoキーワードを使用できます。[12] [13]これにより、初期化子の特定の型の変数が作成されます。
自動some_strange_callable_type = std :: bind ( & some_function , _2 , _1 , some_object );自動other_variable = 5 ;
の型は、特定の引数に対してsome_strange_callable_typeオーバーライドされた特定のテンプレート関数が返す型ですstd::bind。この型は、コンパイラーによってセマンティック分析の一部として手続き的に簡単に判別されますが、ユーザーが検査して判別するのは簡単ではありません。 の型other_variableも明確に定義されていますが、ユーザーが判別するのは簡単です。これは でありint、整数リテラルと同じ型です。
C++ でのこのキーワードの使用はauto、このキーワードのセマンティクスを再利用したものです。このキーワードは元々、型なしの前身言語であるBで、型なしの自動変数定義を示す関連する役割で使用されていました。
さらに、キーワードをdecltype使用して、コンパイル時に式の型を決定することもできます。例:
int some_int ; decltype ( some_int ) other_integer_variable = 5 ;
auto自動変数の型はコンパイラのみが認識するため、と組み合わせるとより便利です。ただし、 は、演算子のオーバーロードや特殊な型
decltypeを多用するコード内の式にも非常に便利です。
autoコードの冗長性を減らすのにも役立ちます。例えば、
( std :: vector < int >:: const_iterator itr = myvec.cbegin (); itr ! = myvec.cend ( ) ; ++ itr )の場合
プログラマーはより短い
( auto itr = myvec.cbegin (); itr ! = myvec.cend ( ) ; ++ itr )の場合
「myvec」は begin/end イテレータを実装しているので、さらに圧縮できます。
( const auto & x : myvec )の場合
この差はプログラマーがコンテナをネストし始めると大きくなりますが、そのような場合には はtypedefコードの量を減らす良い方法です。
によって示される型は、decltypeによって推論される型とは異なる場合がありますauto。
#include <vector> int main () { const std :: vector < int > v ( 1 ); auto a = v [ 0 ]; // a は int 型ですdecltype ( v [ 0 ]) b = 1 ; // b は const int& 型です。これはstd::vector<int>::operator[](size_type)の戻り値の型ですconst auto c = 0 ; // c は int 型ですauto d = c ; // d は int 型ですdecltype ( c ) e ; // e は int 型です。これは c で指定されたエンティティの型ですdecltype (( c )) f = c ; // (c) は lvalue なので、f は int& 型ですdecltype ( 0 ) g ; // 0 は rvalue なので、g は int 型です}
範囲ベースの for ループ
C++11 では、ステートメントの構文が拡張されfor、要素の範囲にわたって簡単に反復処理できるようになります。
int my_array [ 5 ] = { 1 , 2 , 3 , 4 , 5 }; // my_array 内の各要素の値を 2 倍にします: for ( int & x : my_array ) x *= 2 ;
// 同様ですが、配列要素に対しても型推論を使用します
for ( auto & x : my_array ) x *= 2 ;
この形式の はfor「範囲ベースの for」と呼ばれ、リスト内の各要素を反復処理します。これは、C スタイルの配列、初期化リスト、および反復子を返す関数が定義されているすべての型で機能しますbegin()。begin end()/end ペアを持つすべての標準ライブラリ コンテナーは、範囲ベースの for ステートメントで機能します。
ラムダ関数と式
C++11ではラムダ関数と呼ばれる匿名関数を作成する機能が提供されています。[14] これらは次のように定義されています。
[]( int x , int y ) -> int {戻り値x + y ; }
戻り値の型 (-> intこの例では ) は、すべての式が同じ型を返す限り省略できますreturn。ラムダはオプションでクロージャにすることができます。
代替関数構文
標準の C関数宣言構文は、C 言語の機能セットに完全に適合していました。C++ は C から進化しましたが、基本的な構文はそのままに、必要に応じて拡張しました。しかし、C++ が複雑になるにつれて、特にテンプレート関数宣言に関していくつかの制限が明らかになりました。たとえば、C++03 では、これは無効です。
template < class Lhs , class Rhs > Ret entering_func ( const Lhs & lhs , const Rhs & rhs ) { return lhs + rhs ;} //Ret は lhs+rhs の型でなければなりません
型は、型とRetを加算して生成されるものです。前述の の C++11 機能を使用しても、これは不可能です。
LhsRhsdecltype
template < class Lhs , class Rhs > decltype ( lhs + rhs ) addition_func ( const Lhs & lhs , const Rhs & rhs ) { return lhs + rhs ;} //C++11 では無効
lhsおよび はまだ定義されていないため、これは有効な C++ ではありませんrhs。パーサーが関数プロトタイプの残りの部分を解析するまで、これらは有効な識別子にはなりません。
これを回避するために、C++11では、戻り値の型を後置する新しい関数宣言構文が導入されました。[15]
テンプレート< class Lhs , class Rhs > auto entering_func ( const Lhs & lhs , const Rhs & rhs ) -> decltype ( lhs + rhs ) { return lhs + rhs ;}
この構文は、より一般的な関数の宣言と定義に使用できます。
構造体SomeStruct { auto func_name ( int x , int y ) -> int ; };
自動SomeStruct :: func_name ( int x 、int y ) -> int { return x + y ; }
この場合の「auto」キーワードの使用は構文の一部に過ぎず、C++11では自動型推論は実行されません。ただし、C++14以降では、末尾の戻り値の型を完全に削除することができ、コンパイラは戻り値の型を自動的に推論します。[16]
オブジェクト構築の改善
C++03 では、クラスのコンストラクターは、そのクラスの初期化リスト内の他のコンストラクターを呼び出すことはできません。各コンストラクターは、次のように、そのクラス メンバーすべてを自身で構築するか、共通メンバー関数を呼び出す必要があります。
クラスSomeType { public : SomeType ( int new_number ) { Construct ( new_number ); }
SomeType ()
{構築( 42 ); }
private :
void Construct ( int new_number ) { number = new_number ; }
int数値; };
基本クラスのコンストラクターは派生クラスに直接公開できません。基本クラスのコンストラクターが適切である場合でも、各派生クラスはコンストラクターを実装する必要があります。クラスの非定数データ メンバーは、それらのメンバーの宣言のサイトで初期化できません。これらはコンストラクターでのみ初期化できます。
C++11 はこれらすべての問題に対する解決策を提供します。
C++11 では、コンストラクターが他のピア コンストラクターを呼び出すことができます (委任と呼ばれる)。これにより、コンストラクターは最小限の追加コードで他のコンストラクターの動作を利用できます。委任は、 JavaやObjective-Cなどの他の言語でも使用されています。
構文は次のとおりです。
クラスSomeType { int数値;
パブリック:
SomeType ( int new_number ) : number ( new_number ) {} SomeType () : SomeType ( 42 ) {} };
この場合、デフォルト パラメータを作成することで同じ効果を実現できますnew_number。ただし、新しい構文では、デフォルト値 (42) をインターフェイスではなく実装で表現できます。これは、関数パラメータのデフォルト値が呼び出しサイトに「焼き付けられる」ため、ライブラリ コードの保守担当者にとってメリットとなります。一方、コンストラクター委任では、ライブラリを使用するコードを再コンパイルせずに値を変更できるようになります。
これには注意点があります。C++03 では、オブジェクトはコンストラクターの実行が終了した時点で構築されたとみなされますが、C++11 では、いずれかのコンストラクターの実行が終了した時点でオブジェクトが構築されたとみなされます。複数のコンストラクターの実行が許可されるため、各委任コンストラクターは、独自の型の完全に構築されたオブジェクトに対して実行されることになります。派生クラスのコンストラクターは、基本クラスの委任がすべて完了した後に実行されます。
基本クラスのコンストラクターについては、C++11 ではクラスが基本クラスのコンストラクターを継承するように指定できます。したがって、C++11 コンパイラは継承と派生クラスの基本クラスへの転送を実行するコードを生成します。これはオール オア ナッシング機能です。つまり、その基本クラスのコンストラクターがすべて転送されるか、まったく転送されないかのどちらかです。また、継承されたコンストラクターは、派生クラスのコンストラクターのシグネチャと一致する場合はシャドウされ、多重継承には制限があります。クラス コンストラクターは、同じシグネチャのコンストラクターを使用する 2 つのクラスから継承することはできません。
構文は次のとおりです。
クラスBaseClass { public : BaseClass ( int値); };
クラスDerivedClass : public BaseClass { public : using BaseClass :: BaseClass ; };
メンバーの初期化では、C++11 では次の構文が許可されます。
クラスSomeClass { public : SomeClass () {}明示的なSomeClass ( int new_value ) : value ( new_value ) {}
プライベート:
int値= 5 ; };
クラスのコンストラクターは、valueコンストラクターが独自の初期化をオーバーライドしない限り、5 で初期化されます。したがって、上記の空のコンストラクターはvalueクラス定義のとおりに初期化されますが、int を受け取るコンストラクターは指定されたパラメーターに初期化されます。
上記の代入初期化の代わりに、コンストラクターまたは均一初期化を使用することもできます。
明示的なオーバーライドと最終
C++03 では、基本クラス関数をオーバーライドするつもりで、誤って新しい仮想関数を作成してしまう可能性があります。例:
構造体Base {仮想void some_func ( float ); };
構造体Derived : Base { virtual void some_func ( int ); };
Derived::some_funcは、基本クラスのバージョンを置き換えることを意図しているとします。しかし、異なるシグネチャを持っているため、2 番目の仮想関数が作成されます。これは、特にユーザーが基本クラスを変更する場合によく発生する問題です。
C++11 はこの問題を解決するための構文を提供します。
構造体Base {仮想void some_func ( float ); };
struct Derived : Base { virtual void some_func ( int ) override ; // 形式が正しくありません - 基本クラスのメソッドをオーバーライドしません};
特別な識別子はoverride、コンパイラが基本クラスをチェックして、この正確なシグネチャを持つ仮想関数があるかどうかを確認することを意味します。存在しない場合、コンパイラはエラーを示します。
C++11 では、クラスからの継承を禁止したり、派生クラスでのメソッドのオーバーライドを禁止したりする機能も追加されています。これは、特殊な識別子を使用して行われますfinal。例:
構造体Base1final { } ;
struct Derived1 : Base1 { }; // クラス Base1 が final としてマークされているため、形式が正しくありません
構造体Base2 {仮想void f ()最終; };
struct Derived2 : Base2 { void f (); // 仮想関数 Base2::f が final としてマークされているため、形式が正しくありません};
この例では、virtual void f() final;ステートメントは新しい仮想関数を宣言しますが、派生クラスがそれをオーバーライドすることも防止します。また、派生クラスがその特定の関数名とパラメータの組み合わせを使用することを防止する効果もあります。
どちらも言語キーワードではoverrideありませんfinal。技術的には宣言子属性の識別子です。
- これらは、特定の後続コンテキスト(すべての型指定子、アクセス指定子、メンバー宣言(構造体、クラス、列挙型の場合)、および宣言子指定子の後、ただし、コンマで区切られた宣言子のリスト内の各宣言子の初期化またはコード実装の前)で使用された場合にのみ、属性として特別な意味を持ちます。
- 宣言された型シグネチャを変更せず、どのスコープでも新しい識別子を宣言またはオーバーライドしません。
- 認識され受け入れられる宣言子属性は、C++ の将来のバージョンで拡張される可能性があります (一部のコンパイラ固有の拡張機能では、追加された宣言子属性がすでに認識されており、コンパイラにコード生成オプションや最適化のヒントを提供したり、デバッガー、リンカー、およびコンパイル済みコードの配置用にコンパイル済みコードに追加データを生成したり、追加のシステム固有のセキュリティ属性を提供したり、実行時にリフレクション プログラミング(リフレクション) 機能を強化したり、他のプログラミング言語やランタイム システムとの相互運用性のために追加のバインディング情報を提供したりします。これらの拡張機能では、宣言子属性識別子の後に括弧で囲まれたパラメーターを取ることができます。ANSI 準拠のために、これらのコンパイラ固有の拡張機能では二重アンダースコア プレフィックス規則を使用する必要があります)。
- その他の場所では、新しい宣言の有効な識別子として使用できます (アクセス可能な場合は後で使用できます)。
ヌルポインタ定数と型
このセクションおよびこのセクションのみにおいて、「 」はすべて、 「 と評価され、int 型である
0定数式」を意味します。実際には、定数式は任意の整数型にすることができます。0
1972 年の C の黎明期以来、定数は0定数整数とヌル ポインター定数という 2 つの役割を担ってきました。 の 2 つの意味に内在する曖昧さは、0C ではプリプロセッサ マクロ を使用することで対処されNULL、これは通常 または に展開されます((void*)0)。C 0++ では から他のポインター型への暗黙的な変換が禁止されているため、へのvoid *キャストの利点が排除されています。その結果、 のみがヌル ポインター定数として許可されます。これは関数オーバーロードとうまく相互作用しません。
0void *0
void foo ( char * ); void foo ( int );
NULLが として定義されている場合0(C++ では通常そうなります)、ステートメントはfoo(NULL);を呼び出しますがfoo(int)、これはほぼ間違いなくプログラマーの意図したものではなく、コードを表面的に読んだときに示唆されることでもありません。
C++11 では、これを修正するために、区別されたヌルポインタ定数として機能する新しいキーワード を導入しました: nullptr。これは 型 でありnullptr_t、暗黙的に変換可能であり、任意のポインタ型またはメンバーへのポインタ型と比較可能です。 を除いて、整数型とは暗黙的に変換または比較できませんbool。元の提案では、 型の右辺値はnullptr_tに変換できないboolように指定されていましたが、コア言語ワーキンググループは、通常のポインタ型との一貫性を保つために、そのような変換が望ましいと判断しました。提案された文言の変更は、2008 年 6 月に満場一致でワーキングペーパーに採択されました。 [1]同様の提案が C 標準ワーキンググループにも提出され、 C23への組み込みが承認されました。[17]
下位互換性のため、0有効な null ポインター定数のままになります。
char * pc = nullptr ; // OK int * pi = nullptr ; // OK bool b = nullptr ; // OK。b は false です。int i = nullptr ; //エラー
foo ( nullptr ); // foo(int) ではなく、foo(nullptr_t) を呼び出します。/* 上記の例では、 スコープ内に互換性のあるポインター型でオーバーロードしている他の関数がない場合のみ、暗黙的な変換を使用して、foo(nullptr_t) が実際に foo(char *) を呼び出すことに注意してください。複数のオーバーロードが存在する場合、 foo(nullptr_t) の明示的な宣言がない限り 、あいまいであるため解決は失敗します。
C++11 の標準型ヘッダーでは、nullptr_t 型は次のように宣言する必要があります:
typedef decltype(nullptr) nullptr_t;
ただし、次のようには宣言しないでください:
typedef int nullptr_t; // NULL を 0 として定義する必要がある以前のバージョンの C++
typedef void *nullptr_t; // NULL を ((void*)0) として定義する ANSI C
*/
強く型付けされた列挙
C++03 では、列挙型は型安全ではありません。列挙型が異なっていても、実質的には整数です。これにより、異なる列挙型の 2 つの列挙値を比較できます。C++03 が提供する唯一の安全性は、整数または 1 つの列挙型の値が別の列挙型に暗黙的に変換されないことです。さらに、基礎となる整数型は実装定義であるため、列挙のサイズに依存するコードは移植できません。最後に、列挙値は囲むスコープにスコープが限定されます。したがって、同じスコープ内の 2 つの別々の列挙に一致するメンバー名を持たせることはできません。
C++11 では、これらの問題がない特別な列挙の分類が許可されています。これは、enum class(enum structは同義語としても受け入れられます) 宣言を使用して表現されます。
enumクラスEnumeration { Val1 、Val2 、Val3 = 100 、Val4 // = 101 };
この列挙は型安全です。列挙クラスの値は暗黙的に整数に変換されません。したがって、整数と比較することもできません (式はEnumeration::Val4 == 101コンパイル エラーになります)。
enum クラスの基になる型は常に既知です。デフォルトの型は ですint。この例に示すように、これを別の整数型にオーバーライドできます。
列挙型クラスEnum2 : unsigned int { Val1 、Val2 };
古いスタイルの列挙では、値は外側のスコープ内に配置されます。新しいスタイルの列挙では、値は列挙クラス名のスコープ内に配置されます。したがって、上記の例では、Val1は未定義ですが、Enum2::Val1は定義されています。
また、古いスタイルの列挙で明示的なスコープと基になる型の定義を提供できる移行構文もあります。
列挙型Enum3 :符号なしlong { Val1 = 1 、Val2 };
この場合、列挙子の名前は列挙のスコープ ( Enum3::Val1) 内で定義されますが、下位互換性のために、囲むスコープにも配置されます。
C++11 では、列挙型の前方宣言も可能です。以前は、列挙型のサイズがそのメンバーの定義に依存するため、列挙型を前方宣言することはできませんでした。列挙型のサイズが暗黙的または明示的に指定されている限り、前方宣言できます。
enum Enum1 ; // C++03 および C++11 では無効。基になる型を判別できません。enum Enum2 : unsigned int ; // C++11 では有効。基になる型は明示的に指定されます。enum class Enum3 ; // C++11 では有効。基になる型は int です。enum class Enum4 : unsigned int ; // C++11 では有効。enum Enum2 : unsigned short ; // C++11 では無効。Enum2 は以前、異なる基になる型で宣言されていたためです。
直角括弧
C++03 のパーサーは、>>すべてのケースで「 」を右シフト演算子またはストリーム抽出演算子として定義します。ただし、ネストされたテンプレート宣言では、プログラマーが 2 つの右山括弧の間にスペースを入れ忘れる傾向があり、コンパイラ構文エラーが発生します。
C++11 ではパーサーの仕様が改善され、複数の右山括弧は、適切な場合にはテンプレート引数リストを閉じるものとして解釈されるようになりました。これは、“ >”、“ >=”、または “ >>” 二項演算子を使用してパラメータ式を括弧で囲むことでオーバーライドできます。
template < bool Test > class SomeType ; std :: vector < SomeType < 1 > 2 >> x1 ; // SomeType<true> の std::vector として解釈され、// その後に "2 >> x1" が続きますが、これは宣言子の有効な構文ではありません。1 は true です。std :: vector < SomeType < ( 1 > 2 ) >> x1 ; // SomeType<false> の std::vector として解釈され、// その後に宣言子 "x1" が続きますが、これは有効な C++11 構文です。 (1>2) は false です。
明示的な変換演算子
C++98 では、explicit単一引数のコンストラクタが暗黙的な型変換演算子として使用されるのを防ぐため、コンストラクタの修飾子として キーワードが追加されました。ただし、これは実際の変換演算子には何も影響しません。たとえば、スマート ポインター クラスには がありoperator bool()、プリミティブ ポインターのように動作できます。この変換が含まれている場合は、 でテストできますif (smart_ptr_variable)(ポインターが null 以外の場合は true、それ以外の場合は false)。ただし、これにより、他の意図しない変換も可能になります。C++ はbool算術型として定義されているため、暗黙的に整数型や浮動小数点型に変換される可能性があり、ユーザーが意図していない数学演算が可能になります。
C++11 では、explicitキーワードを変換演算子に適用できるようになりました。コンストラクターと同様に、暗黙的な変換でこれらの変換関数を使用することを防止します。ただし、ブール値が特に必要な言語コンテキスト (if ステートメントとループの条件、および論理演算子のオペランド) は明示的な変換としてカウントされるため、bool 変換演算子を使用できます。
たとえば、この機能は safe bool の問題をきれいに解決します。
テンプレートエイリアス
C++03 では、すべての実際のテンプレート引数が指定されたテンプレート特殊化の同義語を含め、別の型の同義語としてのみ typedef を定義できます。typedef テンプレートを作成することはできません。例:
テンプレート< typename First 、typename Second 、int Third >クラスSomeType ;
template < typename Second > typedef SomeType < OtherType , Second , 5 > TypedefName ; // C++03 では無効
これはコンパイルされません。
C++11 では、次の構文でこの機能が追加されました。
テンプレート< typename First 、typename Second 、int Third >クラスSomeType ;
テンプレート< typename Second >ではTypedefName = SomeType < OtherType , Second , 5 >を使用します。
このusing構文は、C++11 では型のエイリアスとしても使用できます。
typedef void ( * FunctionType )( double ); // 古いスタイルusing FunctionType = void ( * )( double ); // 新しく導入された構文
無制限の組合
C++03では、どのタイプのオブジェクトがメンバーになれるかについて制限がありますunion。たとえば、共用体には、非自明なコンストラクタやデストラクタを定義するオブジェクトを含めることはできません。C++11では、これらの制限の一部が解除されています。[2]
unionメンバーに重要な特殊メンバー関数がある場合、コンパイラはそれに相当するメンバー関数を生成しないためunion、手動で定義する必要があります。
これは C++11 で許可されている共用体の簡単な例です。
#include <new> // 配置 'new' に必要です。
構造体 Point { Point () {} Point ( int x , int y ) : x_ ( x ), y_ ( y ) {} int x_ , y_ ; };
union U { int z ; double w ; Point p ; // C++03 では無効、C++11 では有効。U () {} // Point メンバーがあるため、コンストラクター定義が必要になります。U ( const Point & pt ) : p ( pt ) {} // 初期化リストを使用して Point オブジェクトを構築します。U & operator = ( const Point & pt ) { new ( & p ) Point ( pt ); return * this ; } // 配置 'new' を使用して Point オブジェクトを割り当てます。};
変更は現在のルールを緩和するだけなので、既存のコードに支障をきたすことはありません。
コア言語機能の改善
これらの機能により、以前は不可能だった、非常に冗長だった、または移植性のないライブラリを必要としていたことを言語で実行できるようになります。
可変長テンプレート
C++11 では、テンプレートは可変数のテンプレートパラメータを取ることができます。これにより、型安全な可変長関数の定義も可能になります。
新しい文字列リテラル
C++03 では、2 種類の文字列リテラルが提供されています。最初の種類は二重引用符で囲まれ、 型の null 終端配列を生成しますconst char。2 番目は として定義されL""、 型の null 終端配列を生成しますconst wchar_t。ここで、 はwchar_tサイズとセマンティクスが未定義のワイド文字です。どちらのリテラル型も、UTF-8、UTF-16、またはその他の種類のUnicode エンコーディングを使用した文字列リテラルをサポートしていません。
C++11 は、UTF-8、UTF-16、UTF-32の 3 つの Unicode エンコードをサポートしています。 型の定義はchar、少なくとも UTF-8 の 8 ビット コーディングを格納するために必要なサイズであり、コンパイラの基本実行文字セットの任意のメンバーを格納するのに十分な大きさであることを明示的に表現するように変更されました。以前は、C++ 標準自体では後者としてのみ定義され、少なくとも 8 ビットを保証するために C 標準に依存していました。さらに、C++11 では、char16_tと という2 つの新しい文字型が追加されていますchar32_t。これらは、それぞれ UTF-16 と UTF-32 を格納するように設計されています。
サポートされている各エンコーディングの文字列リテラルを作成するには、次のようにします。
u8 「これは UTF-8 文字列です。」
u 「これは UTF-16 文字列です。」
U 「これは UTF-32 文字列です。」
最初の文字列の型は通常の ですconst char[]。2 番目の文字列の型はconst char16_t[](小文字の 'u' プレフィックスに注意) です。3 番目の文字列の型はconst char32_t[](大文字の 'U' プレフィックス) です。
Unicode 文字列リテラルを構築する場合、文字列に Unicode コード ポイントを直接挿入すると便利なことがよくあります。これを行うには、C++11 で次の構文を使用できます。
u8 「これは Unicode 文字です: \u2018 。」
u 「これはより大きな Unicode 文字です: \u2018 。」
U 「これは Unicode 文字です: \U00002018 。」
の後の数字は\u16 進数です。通常のプレフィックスは必要ありません0x。識別子は\u16 ビットの Unicode コード ポイントを表します。32 ビットのコード ポイントを入力するには、\Uと 32 ビットの 16 進数を使用します。有効な Unicode コード ポイントのみを入力できます。たとえば、U+D800 ~ U+DFFF の範囲のコード ポイントは、UTF-16 エンコードのサロゲート ペア用に予約されているため、使用できません。
特にXMLファイル、スクリプト言語、または正規表現のリテラルを使用する場合は、文字列を手動でエスケープしないようにすると便利な場合もあります。C++11 では、生の文字列リテラルが提供されます。
R"(文字列データ\内容 " )" R"区切り文字(文字列データ\内容 " )区切り文字"
最初のケースでは、"(と の間にあるすべてが)"文字列の一部です。"と の\文字をエスケープする必要はありません。 2 番目のケースでは、 で"delimiter(文字列が開始され、)delimiter"に達したときにのみ文字列が終了します。 文字列は、delimiter空の文字列を含め、最大 16 文字の長さの任意の文字列にすることができます。 この文字列には、スペース、制御文字、、、または 文字を含めることはできません(。)この\区切り文字列を使用すると、ユーザーは raw 文字列リテラル内に シーケンスを含めることができます)"。 たとえば、R"delimiter("(a-z)")delimiter"は と同等です"\"(a-z)\""。
生の文字列リテラルは、ワイドリテラルまたは任意の Unicode リテラルプレフィックスと組み合わせることができます。
u8R"XXX(私は「生の UTF-8」文字列です。)XXX" uR"*(これは「生の UTF-16」文字列です。)*" UR"(これは「生の UTF-32」文字列です。)"
ユーザー定義リテラル
C++03 は、多数のリテラルを提供します。文字は、12.5コンパイラによってdouble12.5 の値を持つ型として解決されるリテラルです。ただし、fのようにサフィックス を追加すると、値 12.5 を含む12.5f型の値が作成されますfloat。リテラルのサフィックス修飾子は C++ 仕様によって固定されており、C++03 コードでは新しいリテラル修飾子を作成できません。
対照的に、C++11 では、リテラルが変更する文字列に基づいてオブジェクトを構築する新しい種類のリテラル修飾子をユーザーが定義できるようになりました。
リテラルの変換は、raw と cooking という 2 つの異なるフェーズに再定義されます。raw リテラルは特定のタイプの文字のシーケンスですが、cooked リテラルは別のタイプです。C++ リテラル は、1234raw リテラルとして、この文字のシーケンス 、、、 です'1'。cooked'2'リテラルとしては、整数 1234 です。C++ リテラルの raw 形式は、、ですが、cooked 形式では整数 10 です。
'3''4'0xA'0''x''A'
リテラルは、生の形式と加工された形式の両方で拡張できますが、加工された形式でのみ処理できる文字列リテラルは例外です。この例外は、文字列には、問題の文字の特定の意味とタイプに影響するプレフィックスがあるためです。
すべてのユーザー定義リテラルは接尾辞であり、接頭辞リテラルを定義することはできません。アンダースコア(_)以外の文字で始まる接尾辞はすべて標準で予約されています。したがって、すべてのユーザー定義リテラルには、アンダースコア(_)で始まる接尾辞が必要です。[18]
リテラルの生の形式を処理するユーザー定義リテラルは、 と記述されるリテラル演算子を介して定義されますoperator ""。次に例を示します。
OutputType演算子"" _mysuffix ( const char * literal_string ) { // OutputType には、const char * OutputType ret ( literal_string );を受け取るコンストラクターがあると想定します。 return ret ; }
OutputType some_variable = 1234 _mysuffix ; // OutputType には、double assert ( some_variable . get_value () == 1234.0 )を返す get_value() メソッドがあると想定します。
代入ステートメントは、OutputType some_variable = 1234_mysuffix;ユーザー定義リテラル関数によって定義されたコードを実行します。この関数は"1234"C スタイルの文字列として渡されるため、ヌル終端文字が付きます。
整数および浮動小数点の生のリテラルを処理するための代替メカニズムは、可変長テンプレートを使用することです。
テンプレート< char ... > OutputType演算子"" _tuffix ();
出力タイプsome_variable = 1234 _tuffix ;出力タイプanother_variable = 2.17 _tuffix ;
これにより、リテラル処理関数が としてインスタンス化されますoperator "" _tuffix<'1', '2', '3', '4'>()。この形式では、文字列を終了するヌル文字はありません。これを行う主な目的は、 がconstexpr 構築可能でコピー可能な型であり、リテラル処理関数が関数であるとconstexpr仮定して、C++11 のキーワードを使用して、コンパイラがコンパイル時にリテラルを完全に変換できるようにすることです。
OutputTypeconstexpr
数値リテラルの場合、クックド リテラルの型は、unsigned long long整数リテラル用またはlong double浮動小数点リテラル用のいずれかになります。(注: 符号付きリテラルは、単項プレフィックス演算子としての符号と符号なし数値を含む式として解析されるため、符号付き整数型は必要ありません。) 代替テンプレート形式はありません。
出力タイプ演算子"" _suffix ( unsigned long long );出力タイプ演算子"" _suffix ( long double );
OutputType some_variable = 1234 _suffix ; // 'unsigned long long' オーバーロードを使用します。OutputType another_variable = 3.1416 _suffix ; // 'long double' オーバーロードを使用します。
前述の新しい文字列プレフィックスに従って、文字列リテラルには以下が使用されます。
出力タイプ演算子"" _ssuffix ( const char * string_values , size_t num_chars ) ;出力タイプ演算子"" _ssuffix ( const wchar_t * string_values , size_t num_chars );出力タイプ演算子"" _ssuffix ( const char16_t * string_values , size_t num_chars );出力タイプ演算子"" _ssuffix ( const char32_t * string_values , size_t num_chars );
OutputType some_variable = "1234" _ssuffix ; // 'const char *' オーバーロードを使用します。 OutputType some_variable = u8 " 1234" _ssuffix ; // 'const char *' オーバーロードを使用します。OutputType some_variable = L "1234" _ssuffix ; // 'const wchar_t *' オーバーロードを使用します。OutputType some_variable = u "1234" _ssuffix ; // 'const char16_t *' オーバーロードを使用します。OutputType some_variable = U "1234" _ssuffix ; // 'const char32_t *' オーバーロードを使用します。
代替テンプレート形式はありません。文字リテラルも同様に定義されます。
マルチスレッドメモリモデル
C++11 はマルチスレッド プログラミングのサポートを標準化します。
関係する部分は 2 つあります。プログラム内で複数のスレッドが共存できるようにするメモリ モデルと、スレッド間の相互作用をサポートするライブラリです。(この記事のスレッド機能に関するセクションを参照してください。)
メモリ モデルは、複数のスレッドが同じメモリ位置にアクセスできるタイミングを定義し、1 つのスレッドによる更新が他のスレッドに表示されるタイミングを指定します。
スレッドローカルストレージ
マルチスレッド環境では、各スレッドがいくつかの固有の変数を持つのが一般的です。これは関数のローカル変数ではすでに発生していますが、グローバル変数と静的変数では発生しません。
新しいスレッドローカルストレージ期間 (既存のstatic、dynamic、automaticに加えて) は、ストレージ指定子 によって示されますthread_local。
静的ストレージ期間 (つまり、プログラムの実行全体にわたる有効期間) を持つ可能性のあるオブジェクトには、代わりにスレッド ローカル期間が与えられます。その目的は、他の静的期間変数と同様に、スレッド ローカル オブジェクトをコンストラクターを使用して初期化し、デストラクタを使用して破棄できるようにすることです。
明示的にデフォルト設定された特別なメンバー関数
C++03 では、コンパイラは、クラス自体にデフォルト コンストラクター、コピー コンストラクター、コピー代入演算子 ( operator=)、およびデストラクタを提供します。プログラマは、カスタム バージョンを定義することで、これらのデフォルトをオーバーライドできます。C++ では、すべてのクラスで機能するいくつかのグローバル演算子 ( などoperator new) も定義されており、プログラマはこれをオーバーライドできます。
しかし、これらのデフォルトの作成を制御する方法はほとんどありません。たとえば、クラスを本質的にコピー不可能にするには、プライベートなコピーコンストラクタとコピー代入演算子を宣言し、それらを定義しないことで実行できます。これらの関数を使用しようとすると、One Definition Rule (ODR) に違反します。診断メッセージは必須ではありませんが、[19]違反によりリンカーエラーが発生する可能性があります。
デフォルト コンストラクターの場合、クラスがコンストラクターを使用して定義されている場合、コンパイラはデフォルト コンストラクターを生成しません。これは多くの場合に便利ですが、特殊なコンストラクターとコンパイラによって生成されたデフォルトの両方を使用できることにも便利です。
C++11では、これらの特別なメンバー関数を明示的にデフォルト設定したり削除したりすることができます。[20]たとえば、このクラスはデフォルトコンストラクタを使用できることを明示的に宣言しています。
class SomeType { SomeType () = default ; // デフォルトコンストラクターは明示的に指定されます。SomeType ( OtherType value ); };
明示的に削除された関数
関数は明示的に無効にすることができます。これは暗黙の型変換を防ぐのに役立ちます。= delete指定子は、特定のパラメータ型を持つ関数の呼び出しを禁止するために使用できます。[20]例えば:
void noInt ( double i ); void noInt ( int ) =削除;
noInt()パラメータを使用して を呼び出そうとするとint、 への暗黙の変換が実行されず、コンパイラによって拒否されますdouble。noInt()を使用した の呼び出しはfloat引き続き機能します。
doubleテンプレートを使用
せずに関数を呼び出すことを禁止することができます。
double onlyDouble ( double d ) { return d ;}テンプレート< typename T > double onlyDouble ( T ) = delete ;
呼び出しはonlyDouble(1.0)機能しますが、onlyDouble(1.0f)コンパイラ エラーが発生します。
クラスのメンバー関数とコンストラクタも削除できます。たとえば、コピー コンストラクタを削除することで、クラス オブジェクトのコピーを防ぐことができますoperator =。
クラスNonCopyable { NonCopyable (); NonCopyable ( const NonCopyable & ) = delete ; NonCopyable &演算子= ( const NonCopyable & ) = delete ; };
タイプlong long int
C++03 では、最大の整数型は ですlong int。これは、少なくとも と同じ数の使用可能ビットを持つことが保証されていますint。この結果、long int一般的な実装では のサイズが 64 ビットになり、他の実装では 32 ビットになりました。C++11 では、long long intこの問題に対処するために新しい整数型が追加されました。これは、少なくとも と同じ大きさでありlong int、64 ビット以上であることが保証されています。この型は、もともとC99によって標準 C に導入され、ほとんどの C++ コンパイラーはこれを拡張機能としてすでにサポートしていました。[21] [22]
静的アサーション
C++03 では、アサーションをテストするための 2 つの方法(マクロassertとプリプロセッサ ディレクティブ) が提供されています#error。ただし、どちらもテンプレートで使用するのには適していません。マクロは実行時にアサーションをテストしますが、プリプロセッサ ディレクティブはテンプレートのインスタンス化の前に行われる前処理中にアサーションをテストします。どちらも、テンプレート パラメータに依存するプロパティのテストには適していません。
新しいユーティリティは、新しいキーワードを使用して、コンパイル時にアサーションをテストする新しい方法を導入しますstatic_assert。宣言は次の形式をとります。
static_assert (定数式,エラーメッセージ);
static_assert使用
例をいくつか示します。
static_assert (( GREEKPI > 3.14 ) && ( GREEKPI < 3.15 )、"GREEKPI は不正確です!" );
テンプレート< class T > struct Check { static_assert ( sizeof ( int ) <= sizeof ( T ), "T は十分な大きさではありません!" ); };
テンプレート< class Integral > Integral foo ( Integral x , Integral y ) { static_assert ( std :: is_integral < Integral >:: value , "foo() パラメータは整数型でなければなりません。" ); }
定数式がの場合、falseコンパイラはエラーメッセージを生成します。最初の例はプリプロセッサ指令に似ています#errorが、プリプロセッサは整数型のみをサポートしています。[23]対照的に、2番目の例では、アサーションはテンプレートクラスがインスタンス化されるたびにチェックされますCheck。
静的アサーションは、テンプレート以外でも役立ちます。たとえば、アルゴリズムの特定の実装は、 のサイズがlong longより大きいことに依存する場合がありますがint、これは標準では保証されていません。このような仮定はほとんどのシステムとコンパイラで有効ですが、すべてに有効というわけではありません。
sizeof明示的なオブジェクトなしでクラスのメンバーを操作できるようにする
C++03 では、sizeof演算子は型とオブジェクトに使用できます。ただし、次の操作には使用できません。
構造体SomeType { OtherTypeメンバー; };
sizeof ( SomeType :: member ); // C++03 では動作しません。C++11 では動作します。
これは のサイズを返す必要がありますOtherType。C++03 ではこれが許可されていないため、コンパイル エラーになります。C++11 では許可されます。また、C++11 で導入された演算子でも許可されますalignof。
オブジェクトの配置の制御とクエリ
alignofC++11 では、とを使用して変数のアラインメントを照会および制御できますalignas。
演算子alignofは型を受け取り、型インスタンスを割り当てる必要がある 2 の累乗バイト境界を ( としてstd::size_t) 返します。参照型が指定された場合はalignof、参照先の型の配置を返します。配列の場合は、要素型の配置を返します。
指定子alignasは、変数のメモリ配置を制御します。指定子は定数または型を取ります。型が指定されている場合は、の省略形になります。たとえば、char 配列が float を保持するために適切に配置されるように指定するには、
alignas(T)次のようにします。alignas(alignof(T))
alignas ( float ) unsigned char c [ sizeof ( float )]
ガベージコレクションの実装を許可する
以前の C++ 標準では、 によるプログラマ主導のガベージ コレクションが提供されていましたset_new_handlerが、自動ガベージ コレクションを目的としたオブジェクト到達可能性の定義はありませんでした。C++11 では、ポインタ値が他の値から「安全に導出される」条件が定義されています。実装では、厳密なポインタ安全性の下で動作することを指定する場合があります。その場合、これらの規則に従って導出されないポインタは無効になる可能性があります。
属性
C++11 では、言語のコンパイラ/ツール拡張用に標準化された構文が提供されています。このような拡張は、従来は#pragmaディレクティブまたはベンダー固有のキーワード ( __attribute__GNU や__declspecMicrosoft など) を使用して指定されていました。新しい構文では、追加情報を二重角括弧で囲まれた属性の形式で指定できます。属性は、ソース コードのさまざまな要素に適用できます。
int [[ attr1 ]] i [[ attr2 , attr3 ]];
[[ attr4 ( arg1 , arg2 )]] if ( cond ) { [[ vendor :: attr5 ]] return i ; }
上記の例では、属性はattr1変数の型に適用されi、attr2はattr3変数自体に適用され、はステートメントattr4に適用され、はreturn ステートメントに適用されます。一般に (ただし例外もあります)、名前付きエンティティに指定された属性は、名前の後、エンティティの前に配置されます。それ以外の場合は、上記のように、複数の属性を 1 組の二重角括弧内にリストしたり、属性に追加の引数を提供したり、ベンダー固有の属性名前空間によって属性のスコープを設定したりできます。
ifvendor::attr5
属性には言語の意味的な意味がなく、無視されてもプログラムの意味が変わらないようにすることが推奨されます。属性は、たとえばコンパイラがより良い診断を発行したり、生成されたコードを最適化したりするのに役立つ情報を提供するのに役立ちます。
C++11 自体には、noreturn関数が戻らないことを指定する属性と、carries_dependency関数の引数または戻り値が依存関係を持つことを示すことでマルチスレッド コードの最適化を支援する属性という 2 つの標準属性が用意されています。[説明が必要]
C++ 標準ライブラリの変更
C++11 標準ライブラリには、多数の新機能が導入されました。これらの多くは、古い標準で実装できましたが、一部は (程度の差はあれ) 新しい C++11 コア機能に依存しています。
新しいライブラリの大部分は、2005 年に発行されたC++ 標準委員会のライブラリ技術レポート(TR1 と呼ばれます)というドキュメントで定義されています。現在、名前空間 を使用して、TR1 のさまざまな完全実装と部分実装が利用可能ですstd::tr1。C++11 では、これらは名前空間に移動されましたstd。ただし、TR1 の機能が C++11 標準ライブラリに導入されたため、初期の TR1 バージョンでは利用できなかった C++11 言語機能を使用して、必要に応じてアップグレードされました。また、C++03 では可能だったが元の TR1 仕様には含まれていなかった機能によって強化されている可能性もあります。
標準ライブラリコンポーネントのアップグレード
C++11 には、既存の標準ライブラリ コンポーネントが利用できる新しい言語機能が多数用意されています。たとえば、ほとんどの標準ライブラリ コンテナーは、Rvalue 参照ベースの移動コンストラクター サポートの恩恵を受けられます。これにより、重いコンテナーをすばやく移動したり、コンテナーの内容を新しいメモリ位置に移動したりできます。標準ライブラリ コンポーネントは、必要に応じて新しい C++11 言語機能を使用してアップグレードされました。これには次の機能が含まれますが、これらに限定されるわけではありません。
- 右辺値参照と関連する移動のサポート
- UTF-16 エンコード ユニットと UTF-32 エンコード ユニットの Unicode 文字タイプのサポート
- 可変長テンプレート(完全な転送を可能にするために右辺値参照と組み合わせる)
- コンパイル時の定数式
decltypeexplicit変換演算子- デフォルトまたは削除された関数が宣言されました
さらに、以前の C++ 標準からかなりの時間が経過しました。標準ライブラリを使用するコードが多数作成されました。これにより、標準ライブラリの一部に改善の余地があることが明らかになりました。検討された多くの改善領域の中には、標準ライブラリのアロケータがありました。以前のモデルを補完するために、C++11 には新しいスコープベースのアロケータ モデルが組み込まれました。
スレッド機能
C++03 言語はスレッドをサポートするメモリ モデルを提供しますが、実際にスレッドを使用するための主なサポートは C++11 標準ライブラリに付属しています。
スレッド クラス ( std::thread) が提供され、これは関数オブジェクト(およびそれに渡す一連の引数 (省略可能)) を受け取り、新しいスレッドで実行されます。メンバー関数を介してスレッド結合サポートを提供することで、実行中の別のスレッドが完了するまでスレッドを停止させることができます。メンバー関数によって、プラットフォーム固有の操作std::thread::join()のために、可能な場合は、基礎となるネイティブ スレッド オブジェクトへのアクセスが提供されます。
std::thread::native_handle()
スレッド間の同期のために、適切なミューテックス( std::mutex、std::recursive_mutexなど) と条件変数(std::condition_variableおよびstd::condition_variable_any) がライブラリに追加されています。これらは、リソース取得初期化(RAII) ロック (std::lock_guardおよびstd::unique_lock) とロック アルゴリズムを介してアクセスでき、簡単に使用できます。
高性能で低レベルの作業では、ミューテックスのオーバーヘッドなしでスレッド間の通信が必要になる場合があります。これは、メモリ位置でのアトミック操作を使用して行われます。これらは、操作に必要な最小限のメモリ可視性制約をオプションで指定できます。明示的なメモリ バリアもこの目的に使用できます。
C++11 スレッドライブラリには、スレッド間で非同期の結果を渡したり、そのような非同期の結果を生成する関数呼び出しをラップしたりするためのfutures と promisesstd::packaged_taskも含まれています。futures の提案は、futures を組み合わせて、一連の promises 内の 1 つの promises の完了を確認する方法が欠けているため、批判されました。[24]
スレッド プールなどのさらに高度なスレッド機能は、将来の C++技術レポートに委ねられています。これらは C++11 の一部ではありませんが、最終的な実装は完全にスレッド ライブラリ機能の上に構築される予定です。
新しいstd::async機能は、タスクを実行して に結び付ける便利な方法を提供しますstd::future。ユーザーは、タスクを別のスレッドで非同期に実行するか、値を待機するスレッドで同期的に実行するかを選択できます。デフォルトでは、実装で を選択できます。これにより、オーバーサブスクリプションなしでハードウェアの同時実行性を簡単に利用でき、単純な使用法でスレッド プールの利点の一部が得られます。
タプル型
タプルは、事前に決められた次元の異種オブジェクトから構成されるコレクションです。タプルは、構造体のメンバー変数の一般化と考えることができます。
TR1 タプル型の C++11 バージョンは、可変長テンプレートなどの C++11 機能の恩恵を受けています。合理的に実装するには、TR1 バージョンでは、実装定義の含まれる型の最大数と、かなりのマクロ トリックが必要でした。対照的に、C++11 バージョンの実装では、実装定義の型の最大数を明示的に指定する必要はありません。コンパイラーはテンプレートのインスタンス化に対して内部的に最大再帰深度を持ちますが (これは正常です)、タプルの C++11 バージョンではこの値はユーザーに公開されません。
可変長テンプレートを使用すると、タプル クラスの宣言は次のようになります。
テンプレート< class ... Types >クラスタプル;
タプル型の定義と使用例:
typedef std :: tuple < int , double , long & , const char *> test_tuple ; long lengthy = 12 ; test_tuple proof ( 18 , 6.5 , lengthy , "Ciao!" );
lengthy = std :: get < 0 > ( proof ); // 'lengthy' に値 18 を割り当てます。std :: get < 3 > ( proof ) = " Beautiful!" ; // タプルの 4 番目の要素を変更します。
タプルの内容を定義せずに作成することは可能ですproofが、タプル要素の型にデフォルト コンストラクターがある場合に限ります。さらに、タプルを別のタプルに割り当てることもできます。2 つのタプルの型が同じである場合、各要素型にはコピー コンストラクターが必要です。それ以外の場合は、右側のタプルの各要素型が左側のタプルの対応する要素型に変換可能であるか、左側のタプルの対応する要素型に適切なコンストラクターが必要です。
typedef std :: tuple < int , double , string > tuple_1 t1 ; typedef std :: tuple < char , short , const char * > tuple_2 t2 ( 'X' , 2 , "Hola!" ); t1 = t2 ; // OK、最初の 2 つの要素は変換でき、// 3 番目は 'const char *' から構築できます。
std::make_pairの場合と同様に、型推論を使用して を自動的に作成する がstd::pair存在し、そのようなタプルを宣言するのに役立ちます。 は、タプルを解凍するのに役立つ左辺値参照のタプルを作成します。ここでも役立ちます。例を参照してください。
std::make_tuplestd::tupleautostd::tiestd::ignore
auto record = std :: make_tuple ( "Hari Ram" , "New Delhi" , 3.5 , 'A' ); std :: string name ; float gpa ; char grade ; std :: tie ( name , std :: ignore , gpa , grade ) = record ; // std::ignore は地名を削除するのに役立ちますstd :: cout << name << ' ' << gpa << ' ' << grade << std :: endl ;
関係演算子 (同じ要素数のタプル間) が使用可能であり、タプルの特性をチェックするための 2 つの式が使用可能です (コンパイル時のみ)。
std::tuple_size<T>::valueタプル内の要素の数を返しますT。std::tuple_element<I, T>::typeIタプルのオブジェクト番号の型を返しますT。
ハッシュテーブル
C++ 標準ライブラリにハッシュテーブル(順序なしの連想コンテナ)を含めることは、最も頻繁に要求されるものの 1 つです。これは、時間的な制約のみにより、C++03 では採用されませんでした。ハッシュ テーブルは、最悪の場合 (衝突が多数発生する場合) にはバランス ツリーよりも効率が悪くなりますが、実際の多くのアプリケーションでは、ハッシュ テーブルの方がパフォーマンスが優れています。
衝突は線形連鎖によってのみ管理されます。これは、委員会が、多くの本質的な問題 (特に要素の消去が認められる場合) を引き起こすオープン アドレス指定のソリューションを標準化するのは適切ではないと判断したためです。独自のハッシュ テーブル実装を開発した非標準ライブラリとの名前の衝突を避けるため、接頭辞として「hash」ではなく「unordered」が使用されました。
新しいライブラリには 4 種類のハッシュ テーブルがあり、同じキー (一意のキーまたは同等のキー) を持つ要素を受け入れるかどうか、および各キーを関連付けられた値にマップするかどうかによって区別されます。これらは、unordered_プレフィックス を持つ、既存の 4 つのバイナリ検索ツリーベースの連想コンテナーに対応します。
新しいクラスは、コンテナ クラスのすべての要件を満たし、要素にアクセスするために必要なすべてのメソッド (、、、) を備えてinsertいます。
erasebeginend
この新しい機能には、C++ 言語のコア拡張は必要ありません (ただし、実装ではさまざまな C++11 言語機能が活用されます)。必要なのは、ヘッダーの小さな拡張と、ヘッダーと<functional>の導入だけです。既存の標準クラスに対するその他の変更は必要ありません。また、標準ライブラリの他の拡張機能にも依存しません。
<unordered_set><unordered_map>
std::array と std::forward_list
ハッシュ テーブルに加えて、さらに 2 つのコンテナーが標準ライブラリに追加されました。std::array は固定サイズのコンテナーで、std::vector よりも効率的ですが、C スタイルの配列よりも安全で使いやすいです。std::forward_list は、双方向の反復処理が必要ない場合に、二重リンクの std::list よりもスペース効率の高いストレージを提供する単一リンク リストです。
正規表現
新しいヘッダーで定義された新しいライブラリは<regex>、いくつかの新しいクラスで構成されています。
- 正規表現はテンプレートクラスのインスタンスによって表されます
std::regex。 - 発生はテンプレートクラスのインスタンスによって表され
std::match_results、 - std::regex_iteratorは正規表現のすべての一致を反復処理するために使用されます
関数はstd::regex_search検索に使用され、一方、「検索と置換」ではstd::regex_replace新しい文字列を返す関数が使用されます。[25]
の使用例を次に示しますstd::regex_iterator。
#include <regex> const char * pattern = R " ( [^ ,.\t\n]+ ) " ; // スペース、カンマ、ピリオド、タブ、改行で区切られた単語を検索
std :: regex rgx ( pattern ); // 無効なパターンの場合は例外をスローします
const char * target = "Unseen University - Ankh-Morpork" ;
// regex_iterator を使用して、'pattern' の文字で区切られた 'target' のすべての単語を識別します。
auto iter = std :: cregex_iterator ( target , target + strlen ( target ), rgx );
// シーケンスの終了イテレータを作成する
auto end = std :: cregex_iterator ();
for (; iter != end ; ++ iter ) { std :: string match_str = iter -> str (); std :: cout << match_str << '\n' ; }
ライブラリ<regex>では、既存のヘッダーを変更する必要はなく (ただし、適切な場合は使用します)、コア言語を拡張する必要もありません。POSIX C では、正規表現は C POSIXライブラリ #regex.h経由でも利用できます。
汎用スマートポインタ
C++11では、 TR1 からのstd::unique_ptr改良と がstd::shared_ptr提供され、非推奨になりました。
std::weak_ptrstd::auto_ptr
拡張可能な乱数機能
C 標準ライブラリは、関数を介して疑似乱数をrand生成する機能を提供します。ただし、アルゴリズムはライブラリ ベンダーに完全に委任されています。C++ はこの機能を変更せずに継承しましたが、C++11 では疑似乱数を生成する新しい方法を提供しています。
C++11 の乱数機能は、乱数ジェネレータの状態を保持し、疑似乱数を生成するジェネレータ エンジンと、結果の範囲と数学的分布を決定する分布の 2 つの部分に分かれています。これら 2 つが組み合わされて、乱数ジェネレータ オブジェクトが形成されます。
C 標準とは異なりrand、C++11 メカニズムには 3 つの基本ジェネレータ エンジン アルゴリズムが付属します。
C++11 では、いくつかの標準ディストリビューションも提供されています。
uniform_int_distribution、uniform_real_distribution、bernoulli_distribution、binomial_distribution、geometric_distribution、negative_binomial_distribution、poisson_distribution、exponential_distribution、gamma_distribution、weibull_distribution、extreme_value_distribution、normal_distribution、lognormal_distribution、chi_squared_distribution、cauchy_distribution、fisher_f_distribution、student_t_distribution、discrete_distribution、piecewise_constant_distributionそしてpiecewise_linear_distribution。
ジェネレータと分布は、次の例のように結合されます。
#include <ランダム> #include <関数>
std :: universe_int_distribution < int > distribution ( 0 , 99 ); std :: mt19937 engine ; // メルセンヌツイスター MT19937 auto generator = std :: bind ( distribution , engine ); int random = generator (); // 0 から 99 までの均一な積分変量を生成します。int random2 = distribution ( engine ); // 分布とエンジンオブジェクトを使用して別のサンプルを直接生成します。
ラッパーリファレンス
ラッパー参照は、クラス テンプレートのインスタンスから取得されます。ラッパー参照は、C++ 言語のreference_wrapper通常の参照 (' ') に似ています。任意のオブジェクトからラッパー参照を取得するには、関数テンプレートが使用されます (定数参照が使用されます)。
&refcref
ラッパー参照は、コピーではなくパラメータへの参照が必要な関数テンプレートに特に役立ちます。
// この関数はパラメータ 'r' への参照を受け取り、それを増分します
。void func ( int & r ) { r ++ ; }
// テンプレート関数。
template < class F , class P > void g ( F f , P t ) { f ( t ); }
int main () { int i = 0 ; g ( func , i ); // 'g<void (int &r), int>' がインスタンス化され、 // 'i' は変更されません。std :: cout << i << std :: endl ; // 出力 -> 0
g ( func , std :: ref ( i )); // 'g<void(int &r),reference_wrapper<int>>' がインスタンス化され、// その後 'i' が変更されます。std :: cout << i << std :: endl ; // 出力 -> 1 }
この新しいユーティリティは既存の<functional>ヘッダーに追加され、C++ 言語のさらなる拡張は必要ありませんでした。
関数オブジェクトのポリモーフィックラッパー
関数オブジェクトのポリモーフィックラッパーは、意味と構文の点で関数ポインターに似ていますが、それほど緊密に結合されておらず、ラッパーの引数と互換性のある引数を持つ、呼び出すことができるもの (関数ポインター、メンバー関数ポインター、またはファンクタ) を無差別に参照できます。
例を挙げると、その特徴が明らかになります。
std :: function < int ( int , int ) > func ; //テンプレート クラス 'function'を使用してラッパーを作成します。 std :: plus < int > add ; // 'plus' は 'template<class T> T plus( T, T ) ;' として宣言されます。// この場合、'add' の型は 'int add( int x, int y )' になります。func = add ; // OK - パラメーターと戻り値の型は同じです。
int a = func ( 1 , 2 ); // 注意: ラッパー 'func' がどの関数も参照しない場合は、// 例外 'std::bad_function_call' がスローされます。
std :: function < bool ( short , short ) > func2 ; if ( ! func2 ) { // 'func2' にはまだ関数が割り当てられていないため True です。
bool neighbor ( long x , long y ); func2 = & neighbor ; // OK - パラメータと戻り値の型は変換可能です。
struct Test { bool operator ()( short x , short y ); }; Test car ; func = std :: ref ( car ); // 'std::ref' は、構造体 'car' のメンバー関数 'operator()' のラッパーを返すテンプレート関数です。 } func = func2 ; // OK - パラメーターと戻り値の型は変換可能です。
テンプレート クラスは、C++ 言語を変更することなく、
functionヘッダー内で定義されました。<functional>
メタプログラミングの型特性
メタプログラミングとは、別のプログラム (または自分自身) を作成または変更するプログラムを作成することです。これは、コンパイル中または実行中に発生する可能性があります。C ++ 標準委員会は、テンプレートを介してコンパイル中にメタプログラミングを行うためのライブラリを導入することを決定しました。
以下は、C++03 標準を使用したメタプログラムの例です。整数指数を計算するためのテンプレート インスタンスの 再帰です。
template < int B , int N > struct Pow { // 再帰呼び出しと組み換え。enum { value = B * Pow < B , N -1 >:: value }; };
template < int B > struct Pow < B , 0 > { // ''N == 0'' 終了条件。enum { value = 1 }; }; int quartic_of_three = Pow < 3 , 4 >:: value ;
多くのアルゴリズムは、異なるタイプのデータに対して動作できます。C++ のテンプレートは汎用プログラミングをサポートし、コードをよりコンパクトで便利なものにします。ただし、アルゴリズムでは、使用されているデータ型に関する情報が必要になることがよくあります。この情報は、型特性を使用してテンプレート クラスのインスタンス化中に抽出できます。
型特性は、オブジェクトのカテゴリとクラス (または構造体) のすべての特性を識別できます。これらは新しいヘッダーで定義されます<type_traits>。
次の例では、テンプレート関数「elaborate」があり、指定されたデータ型に応じて、提案された 2 つのアルゴリズムのいずれかをインスタンス化します ( Algorithm::do_it)。
// 最初の操作方法。
template < bool B > struct Algorithm { template < class T1 , class T2 > static int do_it ( T1 & , T2 & ) { /*...*/ } };
// 2 番目の操作方法。
template <> struct Algorithm < true > { template < class T1 , class T2 > static int do_it ( T1 , T2 ) { /*...*/ } };
// 'elaborate' をインスタンス化すると、正しい操作方法が自動的にインスタンス化されます。
template < class T1 , class T2 > int elaborate ( T1 A , T2 B ) { // 'T1' が整数で 'T2' が浮動小数点の場合のみ 2 番目の方法を使用し、それ以外の場合は 1 番目の方法を使用します。return Algorithm < std :: is_integral < T1 >:: value && std :: is_floating_point < T2 >:: value >:: do_it ( A , B ) ; }
ヘッダーで定義された型特性を介して<type_traits>、型変換操作を作成することもできます (テンプレート内では不十分です)
static_cast。const_cast
このタイプのプログラミングでは、簡潔で簡潔なコードが生成されますが、これらの手法の弱点はデバッグです。コンパイル中は扱いにくく、プログラム実行中は非常に困難です。
関数オブジェクトの戻り値の型を計算するための統一された方法
コンパイル時にテンプレート関数オブジェクトの戻り値の型を決定することは、特に戻り値が関数のパラメータに依存する場合は直感的ではありません。例:
struct Clear { int operator ()( int ) const ; // パラメータの型はdouble operator ()( double ) const ; // 戻り値の型と同じです。};
テンプレート< class Obj > class Calculus { public : template < class Arg > Arg operator ()( Arg & a ) const { return member ( a ); } private : Obj member ; };
クラス テンプレートをインスタンス化するとCalculus<Clear>、 の関数オブジェクトはcalculus常に の関数オブジェクトと同じ戻り値の型を持ちますClear。ただし、Confused以下のクラスが与えられた場合:
struct Confused { double operator ()( int ) const ; // パラメータの型がint operator ()( double ) const ; // 戻り値の型と等しくありません。};
インスタンス化を試みると、 のCalculus<Confused>戻り値の型がCalculusクラスの戻り値の型と同じではなくなりますConfused。 コンパイラは からintへの変換double、およびその逆の変換について警告を生成する場合があります。
TR1 では、すべての宣言に対して関数オブジェクトの戻り値の型を決定して使用できるようにするテンプレート クラスが導入され、C++11 ではこれが採用されていますstd::result_of。オブジェクトは、オブジェクトCalculusVer2を使用してstd::result_of関数オブジェクトの戻り値の型を導出します。
テンプレート< class Obj > class CalculusVer2 { public : template < class Arg > typename std :: result_of < Obj ( Arg ) >:: type operator ()( Arg & a ) const { return member ( a ); } private : Obj member ; };
このように、関数オブジェクトのインスタンスではCalculusVer2<Confused>、変換、警告、またはエラーは発生しません。
の TR1 バージョンからの唯一の変更点std::result_ofは、TR1 バージョンでは実装が関数呼び出しの結果の型を判別できないことを許可していたことです。 をサポートするための C++ の変更によりdecltype、 の C++11 バージョンではstd::result_ofこれらの特殊なケースは必要なくなり、実装ではすべてのケースで型を計算する必要があります。
C互換性の向上
C99からのCとの互換性のために、以下が追加された: [26]
- プリプロセッサ: [27]
- 可変長マクロ、
- 隣接するナロー/ワイド文字列リテラルの連結、
_Pragma()– と同等です#pragma。
long long– 少なくとも 64 ビットの長さの整数型。__func__– マクロは、それが含まれる関数の名前に評価されます。- ヘッダー:
cstdbool(stdbool.h)、cstdint(stdint.h)、cinttypes(inttypes.h)。
当初計画されていたが削除されたか含まれていない機能
別の TR へ移動します:
- モジュール
- 10進数型
- 数学特殊関数
延期した:
- コンセプト
- より完全な、または必要なガベージコレクションのサポート
- 反射
- マクロスコープ
削除または廃止された機能
シーケンスポイントという用語は削除され、1つの操作が別の操作の前に順序付けられるか、2つの操作が順序付けられていないかを指定するものに置き換えられました。[28]
キーワードの以前の使用はexport削除されました。[29]キーワード自体は、将来の使用に備えて残されています。
動的例外指定は非推奨です。[29]例外をスローしない関数のコンパイル時の指定はnoexceptキーワードで可能であり、最適化に役立ちます。
std::auto_ptrは非推奨となり、 に置き換えられましたstd::unique_ptr。
関数オブジェクト基本クラス ( std::unary_function、std::binary_function)、関数へのポインターへのアダプター、メンバーへのポインターへのアダプター、およびバインダー クラスはすべて非推奨です。
参照
参考文献
- ^ 「国際標準ができました: C++0x が満場一致で承認されました」。2011 年 8 月 12 日。2018 年 12 月 11 日時点のオリジナルよりアーカイブ。2011年8 月 12 日閲覧。
- ^ ab Stroustrup, Bjarne. 「C++11 FAQ」. stroustrup.com . 2018-10-06 にオリジナルからアーカイブ。2014-10-15に取得。
- ^ ab 「C++11 の概要: 委員会を導いた具体的な設計目標は何か?」。標準 C++。2019 年 1 月 31 日時点のオリジナルよりアーカイブ。2015年 9 月 4 日閲覧。
- ^ 「Bjarne Stroustrup: C++0x の概要」(PDF)。2016 年 6 月 17 日時点のオリジナルよりアーカイブ(PDF) 。2011 年6 月 30 日閲覧。
- ^ 「ISO/IEC 14882:2011」。ISO。2011年9月2日。2013年1月29日時点のオリジナルよりアーカイブ。 2011年9月3日閲覧。
- ^ 「ワーキングドラフト、プログラミング言語 C++ の標準」(PDF)。2019 年 1 月 21 日時点のオリジナルよりアーカイブ(PDF) 。2012 年 4 月 26 日閲覧。
- ^ “The Standard”. 2019年5月13日時点のオリジナルよりアーカイブ。2012年11月2日閲覧。
- ^ “Clang - C++プログラミング言語のステータス”. 2023年11月29日. 2023年11月29日時点のオリジナルよりアーカイブ。2023年12月1日閲覧。
- ^ 「GCC 4.8.1 リリース、C++11 機能が完了: 標準 C++」isocpp.org . 2023 年 12 月 1 日閲覧。
- ^ Sutter、Alexandrescu「C++ コーディング標準」#15
- ^ Gabriel Dos Reis、Bjarne Stroustrup (2010 年 3 月 22 日)。「システムプログラミング言語の一般的な定数式、SAC '10 会議録」(PDF)。2018 年 6 月 13 日時点のオリジナルよりアーカイブ(PDF) 。2012 年8 月 18 日閲覧。
- ^ Jaakko Järvi、Bjarne Stroustrup、Douglas Gregor、Jeremy Siek (2003 年 4 月 28 日)。「Decltype と auto、プログラミング言語 C++、文書番号: N1478=03-0061」(PDF) 。2015年 5 月 28 日時点のオリジナルよりアーカイブ(PDF) 。2015 年6 月 6 日閲覧。
- ^ Roger Orr (2013年6月). 「「自動車 – 必要悪?」Overload Journal #115」。2015年6月6日時点のオリジナルよりアーカイブ。 2015年6月6日閲覧。
- ^ 「文書番号: N1968=06-0038- C++ のラムダ式とクロージャ」(PDF)。オープン標準。2011年 7 月 28 日にオリジナルからアーカイブ(PDF) 。2009年 4 月 20 日に取得。
- ^ 「Decltype (リビジョン 5)」(PDF)。2022 年 2 月 14 日時点のオリジナルよりアーカイブ(PDF) 。2022 年 2 月 16 日閲覧。
- ^ "auto 指定子 (C++11 以降) - cppreference.com". en.cppreference.com . 2016 年 10 月 20 日時点のオリジナルよりアーカイブ。2016年 10 月 18 日閲覧。
- ^ Gustedt, Jens (2019-07-09). 「nullptr 定数の導入 - v1」(PDF) . ISO JTC1/SC22/WG14 文書登録. 国際標準化機構。2020-07-27のオリジナルからアーカイブ(PDF) 。2020-04-19に取得– open-std.org 経由。
- ^これは、 整数リテラルなどの数値リテラルの数字のグループ化にアンダースコアを使用するという提案 (他の言語では一般的) と競合を引き起こしたため、C++14ではグループ化に代わりにアポストロフィ (上部のコンマとして) を使用します。Daveed Vandevoorde (2012-09-21)。「N3448: Painless Digit Separation」(PDF)。2015-08-11 にオリジナルからアーカイブ(PDF) 。2015-08-13に取得。、Lawrence Crowl (2012-12-19)。「N3499: 数字の区切り文字」。2015-08-11 にオリジナルからアーカイブ。2015-08-13に閲覧。
- ^ ISO / IEC (2003). ISO/IEC 14882 :2003(E): プログラミング言語 – C++ §3.2 1つの定義ルール [basic.def.odr]パラグラフ3
- ^ ab 「デフォルトおよび削除された機能 – ISO/IEC JTC1 SC22 WG21 N2210 = 07-0070 – 2007-03-11」。2012年8月19日時点のオリジナルよりアーカイブ。 2012年12月20日閲覧。
- ^ 「GNU コンパイラ コレクション (GCC) の使用: Long Long」。gcc.gnu.org。2016年 8 月 21 日時点のオリジナルよりアーカイブ。2016 年 7 月 25 日閲覧。
- ^ 「データ型の範囲 (C++)」。2009 年 2 月 21 日時点のオリジナルよりアーカイブ。2009 年 4 月 23 日閲覧。
- ^ サミュエル・P・ハービソン3世、ガイ・L・スティール・ジュニア:「C – Aリファレンスマニュアル」、第5版、p.251
- ^ Milewski, Bartosz (2009 年 3 月 3 日). 「Broken promises–C++0x futures」。2011 年 9 月 16 日時点のオリジナルよりアーカイブ。2010年1 月 24 日閲覧。
- ^ 「C++ 正規表現ライブラリ」cppreference.com . 2022年12月10日閲覧。
- ^ 「Clang - C++98、C++11、および C++14 のステータス」。Clang.llvm.org。2013 年 5 月 12 日。2019 年 5 月 28 日時点のオリジナルよりアーカイブ。2013年 6 月 10 日閲覧。
- ^ 「C99 プリプロセッサ同期のワーキングドラフトの変更」www.open-std.org。 2020 年 7 月 31 日時点のオリジナルよりアーカイブ。2014 年 5 月 26 日閲覧。
- ^ Caves, Jonathan (2007 年 6 月 4 日). 「C++-0x 言語標準のアップデート」。2011 年 9 月 9 日時点のオリジナルよりアーカイブ。2010年5 月 25 日閲覧。
- ^ ab Sutter, Herb (2010年3月3日). 「Trip Report: March 2010 ISO C++ Standards Meeting」. 2018年7月11日時点のオリジナルよりアーカイブ。2010年3月24日閲覧。
外部リンク
- C++ 標準委員会
- C++0X: 標準 C++ の新しい顔
- Herb Sutter の C++11 に関するブログ記事
- アンソニー・ウィリアムズの C++11 に関するブログ記事
- ウォータールー大学で Bjarne Stroustrup が行った C++0x に関する講演。2009 年 1 月 23 日にWayback Machineにアーカイブされています。
- 言語の現状: Bjarne Stroustrup 氏へのインタビュー (2008 年 8 月 15 日) 2009 年 1 月 31 日にWayback Machineにアーカイブされました
- C++ 0x コア言語機能とコンパイラでの可用性を追跡するのに役立つ Wiki ページ
- オンライン C++11 標準ライブラリ リファレンス
- オンライン C++11 コンパイラ
- Bjarne Stroustrup の C++11 FAQ
- C++11 の機能に関する詳細情報:範囲ベースの for ループ、auto_ptr が非推奨になった理由など。
