C++11 で拡張された機能について その1

C++11 では、モダンなプログラム言語で使われているような 機能が多く、盛り込まれたので、その一部を紹介します。

nullptr

C++11 から ヌルポインタの表記として NULL ではなく nullptr を 使用することが推奨されます。

nullptrvoid * を拡張した std::nullptr_t を型としてもつ インスタンスになります。これに対して、 C++03 までに使われていた NULL は、一般的には、 #define NULL 0 と定義されたマクロに なります。

従って以下のようなオーバロードされた関数定義があった場合、 valueに NULL を指定した場合、意図通りの呼び出しが行なわれ ません。

void setDescription(char* buffer, int value);    // (1)
void setDescription(char* buffer, void* value);  // (2)

上記の指定、 setDescription(buffer, NULL) を指定した場合、 意図としては、 (2) が呼ばれることを期待しているが、実際には、 (1) が呼ばれます。 setDescription(buffer, nullptr) を指定した 場合には、想定していた (2)が呼ばれます。

また、 std::nullptr_t という専用の型を持つため、ヌルポインタを 明示的にしていするコンストラクタの挙動などが記述しやすくなっています。

初期化記法の統一

C++11では以下のように初期化の記述が統一されました。

int x { 3 };
int a[] { 0, 1, 2, 3 };
struct S1 { int a, b; } s { 0, 1 };
std::vector<int> v { 0, 1, 2, 3 };
pair<int, double> p { 1, 2.3 };
map<string, int> m {{"aaa", 10}, {"bbb", 20}, {"ccc", 30}};

これによって、クラスや、構造体ごとに初期化を確認する必要がなくなりました。 C# などのように直感的な初期化記述が行えるようになりました。

型推論指定の auto の導入

C++11では、変数の型に auto を指定することで、対象の変数の型を初期値から 推論して割り当てる機能をもちます。

以下に推論される型の例をしまします。

auto i = 0;                         // i は int 型
const auto l = 0L;                  // l は const long 型
auto& r = i;                        // r は int& 型
auto s = "";                        // s は const char* 型
auto p = std::make_pair(1, 'c');    // p は std::pair<int, char> 型
auto q = std::make_shared<int>(42); // q は std::shared_ptr<int> 型
auto z = { 1.0, 2.0, 3.0, };        // z は std::initializer_list<double> 型
auto f = []{};                      // f は 引数を取らずに値を返さない operator() を持つユニークなクロージャ型

for-loop の範囲ベース指定

C++11では、イテレータを持つコンテナのインスタンスに対する for-loop は、 モダンなプログラミング言語のように、範囲ベースの指定が行えます。

以下に範囲ベース指定のfor-loopの例を示します。

std::vector<int> v{1, 2, 3, 4, 5, 6, 7};
for(const auto& x : v) {
  std::cout << x << "\n";
}

この範囲ベース指定は、コンテナクラスがイテレータを持っていて、 begin(), end()を整備していれば、動作します。

for-loopの範囲ベース指定は、C++特有のイテレータを上手く隠蔽して くれますし、auto による型推論を併用することで、コンテナの要素の型を 意識しないで記述することが可能です。