Modern C++編【言語解説】 第3章 C言語の機能の強化

先頭へ戻る

このエントリーをはてなブックマークに追加

この章の概要

この章の概要です。

nullptr

C++ には、ヌルポインタを表すリテラルである nullptr が追加されています。 nullptr は、すべてのポインタ型のヌルポインタを表現することができます。

nullptr は、従来の NULLマクロや 0 を使う方法と違い、 「ヌルポインタを表現するリテラル」以外の解釈があり得ないため、より安全に利用することができます。 C++ では、ヌルポインタを表現する際には常に nullptr を使うようにするべきです。

#include <iostream>

int main()
{
    // どんなポインタ型に対してでも使用できる
    int* p1 = nullptr;
    void* p2 = nullptr;
    const void* p3 = nullptr;
    char** p4 = nullptr;

    if (p1 == nullptr) {  // あるいは !p1
        std::cout << "null" << std::endl;
    }
}

実行結果:

null

nullptr は、整数型などの他の型へは暗黙的に変換されることはありませんが、 後述する bool型へは変換されます。

bool

C++ には、真か偽の2通りだけを扱う bool型が追加されています。 また、bool型のリテラルとして、truefalse があり、それぞれ真と偽を表します。

C言語でも、C99 からは stdbool.h をインクルードすることによって、 bool、true、false を使うことができます(C言語編第13章)。 ただし実情は #define による置き換えなので、C++ の bool型のように、型による強制力は働きません。

#include <iostream>

int main()
{
    bool b = true;

    if (b) {
        std::cout << "true" << std::endl;
    }

    b = false;

    if (!b) {
        std::cout << "false" << std::endl;
    }
}

実行結果:

true
false

C言語では、真を 0以外、偽を 0 という整数値で表現してきました。 C++ においても、0以外は真、0 は偽というルールは変わっていませんが、 C++ では、真偽のいずれかを表現する際には、常に bool型を使うべきです。

数値型やポインタ型は、bool型へ暗黙的に変換できます。 0 や、ヌルポインタを表す値は false に変換され、それら以外の値は true に変換されます。

#include <iostream>

int main()
{
    bool x1 = 0;           // 0 は false
    bool x2 = 1;           // 0以外は true
    bool x3 = &x1;         // ヌルでないポインタは true
    bool x4 = nullptr;     // nullptr は false
    bool x5 = 0.001;       // 0以外は true
    bool x6 = 0.0;         // 0 は false

    std::cout << x1 << "\n"
              << x2 << "\n"
              << x3 << "\n"
              << x4 << "\n"
              << x5 << "\n"
              << x6 << std::endl;
}

実行結果:

0
1
1
0
1
0

出力結果が true や false にならないことに不満がある場合は、 std::cout に渡す1つ目の情報を、「std::boolalpha」にすると改善できます。 この機能に関する詳細は、【標準ライブラリ】第16章で解説しています。

    std::cout << std::boolalpha
              << x1 << "\n"
              << x2 << "\n"
              << x3 << "\n"
              << x4 << "\n"
              << x5 << "\n"
              << x6 << std::endl;
false
true
true
false
true
false

なお、bool型のサイズは実装依存です

long long

C++ には、long型以上のサイズを持つ整数型 long long が追加されています。 必ずしも long型よりも大きいという保証はなく、long型「以上」であることに注意して下さい。 最低でも 64bit のサイズを持つことは保証されています。
C言語でも、C99 から追加されています(C言語編第20章)。

他の整数型と同様、signed や unsigned を付加することができます。 また、long long型の整数リテラルを表現するためには、サフィックス「ll」または「LL」を付加します。

#include <iostream>

int main()
{
    long long x1 = -1000000000LL;
    unsigned long long x2 = 1000000000ULL;

    std::cout << x1 << "\n"
              << x2 << std::endl;
}

実行結果:

-1000000000
1000000000

C++14 では、整数リテラルが非常に大きな数になる場合、桁区切り文字を活用することで、 分かりやすく記述することができます。

変数宣言位置の自由化

C++ では、変数の宣言を行う位置が非常に柔軟になっており、 C言語のように、ブロックの先頭でなければならないというルールは撤廃されています。
C言語でも、C99 からはこのルールは撤廃されています(C言語編第22章)。

#include <iostream>
#include <cstring>

int main()
{
    const char* str = "abcde";
    std::cout << str << std::endl;

    std::size_t len = std::strlen(str);
    std::cout << len << std::endl;
}

実行結果:

abcde
5

変数len は、ブロック(この場合、main関数)の先頭にありませんが、C++ では許されます。
また、次のようなことも可能です。

#include <iostream>

int main()
{
	for (int i = 0; i < 5; ++i) {
		std::cout << i << std::endl;
	}
}

実行結果:

0
1
2
3
4

この場合、変数 i のスコープは for文の内側だけに限定されます。

if文でも同様のことが可能です。

#include <iostream>

int func()
{
    return 10;
}

int main()
{
    if (int a = func()) {
        std::cout << a << " is true" << std::endl;
    }
    else {
        std::cout << a << " is false" << std::endl;
    }
}

実行結果:

10 is true


C++ では、初期値を与えずに変数宣言しても、それは定義として扱われます。 つまり、実際にメモリの割り当てが行われますから、変数宣言はその変数が必要になるタイミングぎりぎりまで遅らせるべきです。 そうすることで、プログラムの実行効率が高まる可能性があります。

範囲for文

C++ の for文は拡張され、配列などの要素がまとまったデータ構造を走査しやすくなっています。 この新たな機能は、範囲for文と呼ばれます。

#include <iostream>

int main()
{
    const int array[] = {0, 1, 2, 3, 4};

    for (int v : array) {
        std::cout << v << std::endl;
    }
}

実行結果:

0
1
2
3
4

for の後の ( ) の中に、「:」で区切った2つの項を記述します。 2つ目の項に配列などのデータ構造を置き、1つ目の項には要素を受け取る変数の宣言を置きます。 1回の繰り返しを行うたびに、データ構造から要素を1つ取り出し、変数に格納します。

範囲for文は、配列だけでなく、std::vector(【標準ライブラリ】第6章)のような各種コンテナに対して使うことができます。 結局のところ、std::begin関数、std::end関数(【標準ライブラリ】第17章)が適応できる対象であれば良いです。

範囲for文で要素の値を変更したい場合は、要素の受け取りに使う変数を参照(第18章)にします (array を非const にする必要はあります)。 例えば、「for (int& v : array)」のように書けば、「v = 0;」のように代入することができます。

for文であることに変わりは無いので、break文や continue文も機能します。

#include <iostream>

int main()
{
    const int array[] = {0, 1, 2, 3, 4};

    for (int v : array) {
        if (v == 1) {
            continue;
        }
        if (v == 3) {
            break;
        }

        std::cout << v << std::endl;
    }
}

実行結果:

0
2

可変個引数マクロ

C++ では、通常の関数と同様に、関数形式マクロの引数も可変個にできます。 (C言語でも C99 からはできるようになりました。C言語編第52章)。

#include <cstdio>

#define PRINT_TO_STDERR

#ifdef PRINT_TO_STDERR
#define PRINT(...)  std::fprintf(stderr, __VA_ARGS__)
#else
#define PRINT(...)  std::printf(__VA_ARGS__)
#endif

int main()
{
    PRINT("%d\n", 123);
    PRINT("%s %d\n", "abc", 123);
}

実行結果:

123
abc 123

可変個引数の部分は「...」で表現します。 可変でない引数があれば、通常の関数形式マクロと同様に引数名を並べてから、末尾に「...」を置きます。

置換後の並びについては、__VA_ARGS__ と書いた部分が、可変個引数の部分に指定した部分と対応します。

__func__

C++ では、現在の関数名に置換される事前定義識別子 __func__ が追加されています。 (C言語でも C99 で追加されました。C言語編第29章)。

#include <iostream>

void func()
{
    std::cout << __func__ << std::endl;
}

int main()
{
    std::cout << __func__ << std::endl;
    
    func();
}

実行結果:

func
main

__func__ がどのようなフォーマットの文字列になるかは環境依存です。

C++14 (2進数リテラル)

C++14

C++14 で、2進数の整数リテラルを表現することができるようになりました。 プリフィックスとして、「0b」または「0B」を付加します。

#include <iostream>

int main()
{
    std::cout << 0b1100 << std::endl;
    std::cout << 0B1001 << std::endl;
}

実行結果:

12
9

C++14 (桁区切り文字)

C++14

C++14 では、整数リテラルや浮動小数点数リテラルの途中に、 任意の位置で「'」を入れ込むことができるようになりました。 3桁ごとのコンマ(,) の代わりに使うなどして、大きな数を読みやすくすることができます。

#include <iostream>

int main()
{
    std::cout << 1'000'000 << std::endl;
    std::cout << 0b1111'0000 << std::endl;
}

実行結果:

1000000
240

C++17 (16進数浮動小数点数リテラル)

C++17

C++17 で、浮動小数点数リテラルを 16進数で表記することができるようになりました。 C言語では C99 から同じようなことができます(C言語編第20章)。

#include <iostream>

int main()
{
    std::cout << 0xABC.DEFp-2 << std::endl;
}

実行結果:

687.218

「0x」または「0X」のプリフィックスを付け、仮数部と指数部の間に「p」または「P」を置きます。 なお、指数部については 10進数で表記し、底は 2 です

10進数の通常の浮動小数点数リテラルでは、仮数部と指数部の区切りに「e」や「E」を使いますが、 16進数を表すアルファベット(a~f) の中に含まれてしまっているため、「p」や「P」に変更されています。

この機能は、VisualC++ 2017 では使用できません。


練習問題

問題① bool型や、各種整数型のサイズを調べるプログラムを作成して、自分が使っているコンパイラでのサイズを確認して下さい。

問題② 指定した文字列と、呼び出し位置の関数名を出力するログ出力マクロを作成して下さい。

問題③ 次のプログラムの実行結果を答えて下さい。

#include <iostream>

int main()
{
    int array[] = {0, 1, 2, 3, 4};

    for (int v : array) {
        v *= v;
    }

    for (int v : array) {
        std::cout << v << std::endl;
    }
}


解答ページはこちら

参考リンク

プログラミング言語C++ 第4版
 -- C++ とC言語の互換性に関する章がある。また全体を通して、C++ の詳細な仕様に関する解説を行っている。
Effective Modern C++
 -- nullptr を使う方が良い理由について、詳しい解説がある。
S・P・ハービソン3世とG・L・スティール・ジュニアのCリファレンスマニュアル 第5版
 -- C言語と C++ の違いについても触れている。

更新履歴

'2018/1/5 コンパイラの対応状況について、対応している場合は明記しない方針にした。

'2017/7/30 clang 3.7 (Xcode 7.3) を、Xcode 8.3.3 に置き換え。

'2017/7/6 新規作成。



前の章へ

次の章へ

Modern C++編のトップページへ

Programming Place Plus のトップページへ