「プログラミング言語で最も重要なのはその名前です。
言語は良い名前がなければ成功しません。
私は最近非常に良い名前を思いつきました、
今では適切な言語を発明することが残っています。」
D. E.クナット
C ++ 09 Evolution:フラッシュバック
1998年に批准された最初のC ++仕様の後、5年間の休憩が取られ、コンパイラ開発者が標準に適応できるようになりました。 また、今回の「無線沈黙」により、委員会は文書に関するフィードバックを受け取ることができました。 この期間の終わりに、ANSI標準委員会は、修正とさまざまな改善を含む更新された仕様をリリースしました。 これらの修正は、2003年の最初の技術的エラーリストに記載されています。
さらに、委員会のメンバーはC ++の変更に関する提案を受け入れ始めました。 このイニシアチブはC ++ 0xと呼ばれ、21世紀の最初の10年で新しいバージョンの言語が承認されることが予想されていました。 しかし、時間が経つにつれて明らかになりました。新しいバージョンの言語は2009年よりも早く批准できなかったため、このイニシアチブはその名前をC ++ 09に変更しました。
ライブラリ拡張に関するドキュメントの開発は2004年に開始され、2005年1月に完了しました(TR1と呼ばれていました)。 標準C ++ライブラリにいくつかの拡張機能を導入することを推奨しましたが、その多くはBoostフレームワークからのものです。 2006年4月、委員会はすべてのTR1勧告を採用しました(コンパイラ開発者が実装するのが困難な高レベルの数学ライブラリを除きます)。 GCC 4.0は、ほとんどのTR1をstd :: tr1 ::名前空間に既に実装しています。 GCCに加えて、Metrowerks CodeWarrior 9および10もこれを実行し、MicrosoftはVisual C ++ 2008 Feature Packをリリースし、TR1も実装しています(
waskerのおかげです )。
標準化開発委員会は、2007年末までにC ++ 09を完了する予定です。 同じ年に、4月と10月に2つの会議が計画されています。 障害がなければ、ドキュメントの最終版は2008年に利用可能になり、2009年のいつかで批准が行われます。
哲学C ++ 09
約10年前にC ++ 98を批准した後、標準化委員会は言語に大きな変更を加えることに関心がありませんでしたが、初心者が学習しやすく、よりアクセスしやすいものにする変更をサポートしました。 多くのプログラマーは、特定のプログラミング言語の専門家になりたくありません。 それどころか、彼らは彼らの分野の専門家になりたいと思っており、単にタスクを実現する手段としてC ++を使用しています。 強力な新しいツールが言語に追加されましたが、その主な目標は依然として言語を簡素化することです。
別の目標は、言語自体のコアを変更する前に標準ライブラリを簡単に更新することです。 カーネルの変更は非常に危険であり、大きな互換性の問題につながる可能性があります。 それどころか、ライブラリの改善により、より少ないリスクで優れた柔軟性を実現できます。 たとえば、ガベージコレクターの実装を考えてみましょう:セルフクリーニング用の言語のカーネルを変更すると(JavaおよびC#で行われるように)、より深刻な変更が発生し、下位互換性がさらに必要になります。また、標準ライブラリのスマートポインタークラスのサポートにより、低コストでの機会。
最後に、委員会は可能な限り真の生産性を向上させるよう努めました。 C ++の強みの1つは、そのパフォーマンス(比較的新しいC#とJava)です。 そのため、多くのプログラマーがC ++を主要なプログラミング言語として選択しています。 IDCによると、2003年には約300万人のC ++プログラマーがいたため、言語をそうでないものに変えようとするのではなく、利便性のために言語を改善することは理にかなっています。
EC ++の有益な物語
1999年、日本の組み込み開発者のコンソーシアム(NEC、日立、富士通、東芝を含む)が、C ++のサブセットを強調する提案を提出しました。 このサブセットは、コンソーシアムのメンバーによると非常に複雑でパフォーマンスに大きなダメージを与えた特定の数の言語機能を削除することを除いて、C ++をほぼ繰り返します。 削除する必要がある言語の主要コンポーネント:多重継承、パターン、例外、RTTI、新しいキャスト演算子、名前空間。 このような言語のサブセットは、Embedded C ++(または単にEC ++)と呼ばれます。
コンソーシアムの驚いたことに、EC ++コンパイラはC ++コンパイラよりも高速ではありませんでした。 それどころか、場合によってははるかに遅くなりました。 C ++の創設者であるBjarn Straustrupは、テンプレートがほとんどの標準ライブラリで使用されており、それらの削除は完全に非実用的であると説明しました。 同時に、コンソーシアムは、テンプレートをサポートする拡張(拡張)EC ++の出現が可能であると発表しました。
拡張EC ++コンパイラーが利用可能になったとき、それらは再びより大きな同等物と一致しました。 コンソーシアムの驚いたことに、C ++と比較したパフォーマンスの向上はごくわずかでした。 問題の一部は、コンソーシアムがC ++の原則「使用していないものにお金を払う必要がない」を無視したことです。 その後、ISOはEC ++に関する提案を受け入れることを拒否しました。
2004年、EC ++の大失敗に触発されたC ++ 0x委員会は、どのC ++機能に実際に大きなパフォーマンスの問題があるかを判断しようとしました。 結局のところ、生産性を本当に高めることができるのは次の3つの領域だけです。
新規および削除
RTTI(typeid()およびdynamic_cast <>)
例外(スローおよびキャッチ)
メモリ割り当てはパフォーマンスに最も大きな影響を与えることが判明しましたが、ヒープからメモリを割り当てない言語を使用することはほとんどありません。 RTTIと例外処理に関して、多くのコンパイラにはそれらをオフにするスイッチがあります。 最新のコンパイラでは、例外処理はかなり高いレベルで実装されており、問題はRTTIのみにあります。 いずれにせよ、C ++の原則に従う場合、言語の一部の機能を削除することは、それらを無効にすることに匹敵します。
EC ++に関して、Straustrupは次のように述べています。
「私の意見では、EC ++は死んでいますが、そうでなければ死んでいるはずです。
」修正と改善
1998年のC ++標準はそれ自体が目覚しい成果であったにもかかわらず、いくつかの問題がありました。 キャッチが困難なものもあれば、既知の問題もありましたが、多くの場合、新しい解決策を作成するには不十分でした。 Bjarn Straustrupがそれらのいくつかを説明しました。例えば:
ベクトル<ベクトル<int >> xv; //可能です!
vector <double> xv {1.2、2.3、3.4}; // STLコンテナを初期化します
列挙型のより強力な型//列挙型はスコープ内に残ります
テンプレートの外部化//翻訳ユニット間での重複なし
上記のエラーが発生する理由がわからない場合は、発生の理由を理解しようとしないことをお勧めします。 最初のエラーのみを検討します。 欠点は、C ++ 98がベクトルの「>>」部分を右シフト演算子として解析し、エラーを生成することです。 C ++ 09では、このエラーは修正されます。 以下に小さな例を示します。
テンプレート<int I>
struct myX
{
static int const x = 2;
}
テンプレート<>
struct myX <0>
{
typedef int x;
}
テンプレート<typename T>
struct myY
{
static int const x = 3;
}
static int const x = 4;
cout <<(myY <myX <1 >> :: x> :: x> :: x)<< endl; // C ++ 98は「3」を出力し、C ++ 09-「2」を出力します
ANSI / ISO C99との同期
C ++の批准から1年後、ANSI / ISO C仕様はC言語にいくつかの変更を加えて更新され、これらの変更の多くは既にC ++で行われていますが、Cでも意味があります。 対照的に、他のものはC ++の一部ではありませんでしたが、委員会はそれらを価値があると見なし、C ++ 09仕様に反映しようとしました。 これらには以下が含まれます。
__func__ //配置されている関数の名前を返します
long long // 64ビットの数値に一般的に使用される拡張組み込み型
int16_t、int32_t、inttpr_tなど //特定の種類の数値
double x = 0x1.F0 // 16進浮動小数点数
いくつかの数学関数の複雑なバージョン
固定数の引数を取るマクロ
C ++標準ライブラリの機能強化
標準C ++ライブラリ(STLを含む)は、多数の便利なコンテナとユーティリティです。 その機能は充実しているにもかかわらず、ユーザーにとって非常に必要な多くのコンポーネントがありました。 C ++ 09は、これらのスペースを次の新しいライブラリクラスで埋めます。
regex:すべてが期待する正規表現クラス
配列<>:独自のサイズを含む1次元配列(0の場合があります)
tuple <>:tuple-tupleテンプレートクラス
STLハッシュコンテナークラス:unordered_set <>、unordered_map <>
GCC 4(Xcode 2.x)を使用する開発者は、std :: tr1 ::を介して同様の拡張機能を使用できるため、標準ライブラリのこのような変更を2009年まで待つことはできません。
ストリームの改善
ローカルストレージ:
スレッドint x = 1; //スレッド内でグローバルに
原子操作:
アトミック
{
//実行時に他のスレッドを中断します
}
並列実行:
アクティブ
{
{...} //最初の並列ブロック
{...} // 2番目の並列ブロック
{...} // 3番目の並列ブロック
}
並列実行の場合、コンパイラがこの場所で並列ブロックは収益性がないと考える場合、コンパイラはそれらを単に連続して実行する可能性があります。
このような言語の機能は、pthread、mutexなどを使用して実行される開発を大幅に簡素化することは明らかです。 前述の特性は、C ++ 09委員会のメンバーによってまだ議論されているため、若干の変更が行われる可能性があることに注意してください。
このドキュメントでそのような改良に関する詳細を見つけることができ
ます 。
さまざまな数の引数を持つテンプレート
長年の間、C言語では、関数が固定されていない数のパラメーターを持つことができました。 残念ながら、これはC ++ 98では不可能でした。 C ++ 09では、テンプレートにさまざまなタイプの型を含めることができます。 最も簡単な例を次に示します。
// DEBUGフラグが設定されている場合にのみstderrに出力します
テンプレート<typename TypeArgs>
void DebugMessage(TypeArgs ... args)
{
#ifdef DEBUG
// stderrのエントリの実装
#else
//何もしません
#endif
}
//さらにコードで
DebugMessage( "n is"、n);
DebugMessage( "x is"、x、 "y is"、y、 "z is"、z);
DebugMessage( "これは私のトレース:"、
"time ="、クロック()、
「ファイル名=」、__ FILE__、
「行番号=」、「__ LINE__」、
「内部関数:」、__ func__);
コンストラクターの委任
C#などの他の言語では、1つのクラスコンストラクターが別のクラスコンストラクターを呼び出すことができます。 C ++ 98ではこれは不可能であったため、クラス開発者は別の初期化関数を作成する必要がありました。 C ++ 09では、次のコードに示すように、これが可能になります。
クラスMyClass
{
公開:
MyClass(); //デフォルトのコンストラクター
MyClass(void * myptr); //ポインタを取得します
MyClass(int myvalue); //数値を取得します
};
MyClass :: MyClass():MyClass(NULL)// Xを呼び出す(void *)
{
... //コード
}
MyClass :: MyClass(void * myptr):MyClass(0)//呼び出しX(int)
{
... //コード
}
MyClass :: MyClass(int myvalue)//委任されていません
{
... //コード
}
ヌルポインター
ANSIでは、C NULLは(void *)0として定義されます。C++では、NULLは推奨されません。 なんで? Cとは異なり、C ++では、他の型のポインターにvoidポインターを割り当てるのは間違っています。
void * vPtr = NULL; // CとC ++の両方で修正
int * viPtr = NULL; // Cでは正しいが、C ++では間違っている
// C ++ではint *にvoid *を添付できません!
int * viPtr = 0; //右C ++
ただし、C ++コードでのNULLの分布は非常に大きいため、多くのコンパイラは、そのような割り当てが発生すると警告(エラーではなく)を生成するだけです。 C ++でNULLを0として再定義し、キャストエラーの発生を防ぐものもあります。 コンパイラのすべての「礼儀」にもかかわらず、これはすべて初心者のC ++プログラマを大きく混乱させます。
ボイドバー(int); //整数値を取得します
ボイドバー(char *); // charを取得します*
バー(0); //ポインタですか、それとも単なる数字ですか?
バー(NULL); //対応するプロトタイプはありません
したがって、nullポインターの使用を簡素化するために、nullptrがC ++ 09で導入されました。 任意の型のポインターで使用できますが、組み込み型には適用できません。
char * cPtr1 = nullptr; // C ++のNULLポインター
char * vcPtr2 = 0; // True、ただし推奨されません
int n = nullptr; //無効です
myX * xPtr = nullptr; //任意のタイプのポインターで使用できます
ボイドバー(int); //整数値を取得します
ボイドバー(char *); // charを取得します*
バー(0); // foo(int)を呼び出します
バー(nullptr); // fooを呼び出します(char *)
どこに行った?
Cがちょうど開発中であったとき、autoキーワードを使用して、コンパイラーにスタック上の変数の場所を通知しました。例えば:
auto x; / * x(整数)という名前の変数がスタックにあります* /
ANSI Cが1989年に批准されたとき、型定義は削除されました。
auto x; / * ANSI Cでは無効* /
int x; / * True * /
auto int x; / *確かに、真実は不必要です* /
それ以来、1970年代からautoを使用した人はほとんどいませんでしたが、autoはC(およびC ++で)のキーワードになりました。 30年後、C ++ 09標準はautoキーワードを再導入します。 このキーワードで定義された変数は、初期化時に型を自動的に取得します。
auto y = 10.0; // yは浮動小数点数です
auto z = 10LL; // zはlong long
const auto * p =&y; // pはconst double *
収益性は、次のような複雑なビューと組み合わせて使用すると、より明確になります。
void * bar(const int doubleArray [64] [16]);
auto myFcnPtr = bar; // myFcnPtrのタイプが「void *(const int(*)[16])」になりました
その上、autoは、型がそれほど重要ではない一時変数に対して非常に便利になります。 STLコンテナの要素を通過する次の関数を検討してください。
ボイドバー(ベクター<MySpace :: MyClass *> x)
{
for(auto ptr = x.begin(); ptr!= x.end(); ptr ++)
{
... //さまざまな種類のコード
}
}
autoキーワードがない場合、ptr変数の型はvector <MySpace :: MyClass *> :: iteratorになります。 さらに、このコンテナーの変更(たとえば、ベクター<>からリスト<>への変更、クラス名、名前空間名の変更など)は、ループ内でそのタイプが絶対に重要ではないという事実にもかかわらず、プログラマーに確実にptr変数の定義を変更させます。
興味深いことに、C#では、varキーワードを使用して同様の動作が作成されます。
C ++ 09でautoを使用するには、まだ初期化が必要であることに注意してください。
auto x; // C ++ 09で無効
しかし、(別の変数に基づいて)必要な型を知っていたが、初期化したくなかったとしますか? 新しいdecltypeキーワードは、次のコードスニペットなどの目的で使用できます。
bool SelectionSort(二重データ[256]、二重許容値);
bool BubbleSort(二重データ[256]、二重許容値);
bool QuikSort(二重データ[256]、二重許容値);
decltype(SelectionSort)mySortFcn;
if(bUseSelectionSort)
mySortFcn = SelectionSort;
else if(bUseBubbleSort)
mySortFcn = BubbleSort;
他に
mySortFcn = QuikSort;
スマートポインター
スマートポインターは、プログラマーに依存せずに、メモリから自分自身を削除する必要がある時間を自身で理解できるオブジェクトです。 JavaやC#などのほとんどすべての最新言語は、不必要なメモリリークを回避する同様のモデルに従ってメモリを管理します。 C ++ 98では、同様の小さなオブジェクトauto_ptr <>がありました。 残念ながら、auto_ptr <>にはいくつかの制限がありますが、最も顕著なのは、独自のアクセス分散モデルの使用です。 つまり、最後のauto_ptr <>がメモリの唯一の所有者でした。
auto_ptr <int> ptr1(新しいint [1024]);
auto_ptr <int> ptr2 = ptr1;
このため、C ++コミュニティでは、auto_ptr <>の使用回数はゼロになる傾向があります。
C ++ 09標準ライブラリには、よりスマートなポインタビューshared_ptr <>が導入されています。 auto_ptr <>との主な違いは、分散権利モデルと参照カウンターを使用して、いつメモリを解放するかを決定することです。 例:
メイン()
{
shared_ptr <int> ptr1; //スマートNULLポインター
...
{
shared_ptr <int> ptr2(新しいint [1024]);
ptr1 = ptr2; //所有物の分布(封建)
}
// ptr2が削除され、ptr1のみが残ります
//メモリはまだ解放されていません
}
// ptr1が削除され、メモリが解放されました
shared_ptr <>はポインターと見なすことができるため、* ptrとして使用でき、ptr1-> foo()などの構造で使用できます。
明示的なshared_ptr <T>(T * ptr); //メモリにアタッチします
shared_ptr <T>(T * ptr、Fcn delFcn); //メモリとユーザー定義のクリーンアップ関数にアタッチします
shared_ptr <T>(shared_ptr <T> ptr); //コンストラクタをコピーします
shared_ptr <T>(auto_ptr <T> ptr); // auto_ptrで変換<>
最後のコンストラクタがauto_ptr <>からshared_ptr <>にデータを変換することに注意してください。これにより、以前のバージョンのコードからの移行が大幅に容易になり、下位互換性が提供されます。 swap()、static_pointer_cast()、dynamic_pointer_cast()など、いくつかの追加機能が提供されています。
shared_ptr <>はすでにstd :: tr1 ::名前空間の一部であり、MacプログラマーはXcode 2.x以降で使用できます。
右辺値リンク
Cでは、関数パラメーターは常に値で渡されます。つまり、パラメーターのコピーが渡されますが、その値は関係ありません。 Cの変数を変更するには、例のように、関数はポインターを渡す必要があります。
void foo(int valueParameter、int * pointerParameter)
{
++ valueParameter; //パラメータは値で渡され、ローカルコピーが変更されます
++ pointerParameter; //ポインタは値で渡されますが、ローカルコピーはとにかく変更されます
++ * pointerParameter; //この変更は一定のまま
}
C ++の最も強力な機能の1つは、
&演算子を使用して参照によってパラメーターを渡すことでした。 これにより、ポインターを使用せずにパラメーターデータを直接変更できました。
void foo(int valueParameter、int&referenceParameter)
{
++ valueParameter; //パラメータは値で渡され、ローカルコピーが変更されます
++ referenceParameter; //参照渡しで、変更は一定のまま
}
リンクは、右辺値(読み取り専用)ではなく、左辺値(これらは変更可能な変数であるため)に適用する必要があります。
int myIntA = 10;
int myIntB = 20;
foo(myIntA、myIntB); // myIntA = 10、myIntB = 21
foo(1、myIntA); //値で渡される1、myIntA = 11
foo(myIntA、1); //エラー:1は右辺値であり、渡すことはできません
foo(0、myIntB + 1); //エラー:myIntB + 1は右辺値であり、渡すことはできません
内容を変更する必要がない場合でも、参照によってパラメーターを渡すと便利な場合があります。 これは、大きなクラスまたは構造が関数に渡され、そのような巨大なオブジェクトのコピーを避ける必要がある場合に特に優れたソリューションです。
void foo(BigClass valueParameter、const BigClassおよびconstRefParameter)
{
++ valueParameter; //値渡し、変更は一時的
++ constRefParameter; //エラー:定数パラメータを変更できません
}
C ++ 09では、右辺値リンクと呼ばれる新しいタイプのリンクが導入されました(したがって、C ++ 98でおなじみのリンクタイプは左辺値リンクと呼ばれるようになります)。 右辺値リンクは一時データに添付できますが、コピーせずに直接変更できます。
&&演算子は、リンクが右辺値リンクであると言います。
void foo(int valueParameter、int&lvalRefParameter、int && rvalRefParameter)
{
++ valueParameter; //パラメータは値で渡され、すべての変更はローカルです
++ lvalRefParameter; //左辺値リンク、すべての変更は永続的
++ rvalRefParameter; //右辺値リンク、コピーを必要としないローカル変更
}
foo(0、myIntA、myIntB + 1); //一時的な値myIntB + 1はコピーされませんが、渡すことができます
右辺値リンクの主な利点の1つは、移動のセマンティクスを利用できることです。つまり、コピーせずに1つの変数から別の変数にデータを移動できます。 クラスは、コピーコンストラクターの代わりに、またはコピーコンストラクターと共に移動コンストラクターを定義できます。
//クラス定義
クラスX
{
公開:
X(); //デフォルトのコンストラクター
X(定数X&x); //コンストラクタをコピーします(左辺値参照)
X(X && x); //コンストラクターを移動します(右辺値参照)
};
// Xを返すさまざまな関数
Xバー();
X x1; //デフォルトのコンストラクタを使用してx1オブジェクトを作成します
X x2(x1); // x2はx1のコピーになります
X x3(バー()); // bar()は一時的なXを返し、メモリは直接x3に移動します
動作のセマンティクスの主な動機は、生産性の向上です。 2つの行ベクトルがあり、それらの間でデータを交換するとします。 標準のコピーロジックを使用して、次のコードを取得します。
void SwapData(ベクター<string>&v1、ベクター<string>&v2)
{
vector <string> temp = v1; //新しいコピーv1
v1 = v2; //新しいコピーv2
v2 = temp; //一時のNoviaコピー
};
移動のロジックを使用すると、次のようなものが得られます。
void SwapData(ベクター<string>&v1、ベクター<string>&v2)
{
ベクター<string> temp =(ベクター<string> &&)v1; // temp-v1と同じデータ
v1 =(ベクター<string> &&)v2; // v1にはv2が含まれます
v2 =(ベクター<string> &&)temp; // v2は一時データを指します
}
//単一のコピーではなく、移動のみが作成されました!
C ++ 09のその他の追加
説明した機能に加えて、他の変更点のリストを以下に示します。
新しい文字タイプ :chart16_t、char32_t
静的ステートメント (アサート、Boost ::から)
エイリアステンプレート
型チェック :is_pointer()、is_same()
Foreachの紹介
新しい乱数ジェネレーター
結論
C ++の次のバージョンへの多くの変更は、標準ライブラリに関連しているという事実により、プログラマーが利用できるようになりました。 それにもかかわらず、言語の今後の更新に備えることは今価値があります。 これらすべての変更について読むと、C ++にはかなり興味深い未来があることが明らかになります。