2005年9月 6日

C++ のキャストと一時オブジェクト

C++ を学んでいます。Ruby の学びやすさとコード書きやすさを高速道路とすれば、C++ は落とし穴だらけのあぜ道という感じがします。今日、Effective STL (の第22項) を読んでいてなるほどと思ったのはキャストするとコピーが作成されるときがある、ということです。

 
#include <iostream>
using namespace std;

class Foo {
    int x;
public:
    Foo() : x(0) {}
    void inc() { x++; }
    void show() const { cout << x << endl; }
};

int main() {
    Foo foo;
    foo.show();  // 0
    foo.inc();
    foo.show();  // 1
    static_cast<Foo>(foo).inc();
    foo.show();  // 1  変わらない!
    static_cast<Foo&>(foo).inc();
    foo.show();  // 2  Foo& へのキャストなら変わる
    return 0;
}

static_cast<Foo>(foo).inc() の行では 、名前のない一時オブジェクトとして foo のコピーが作成され、コピーに対して inc() メンバ関数が呼ばれます。このため、foo の状態 x は変わりません。

コピーの作成はコピーコンストラクタで行われるため、Foo(const Foo&) をプライベートにすると static_cast<Foo>(foo) はコンパイルエラーになります。

キャストによってコピーが発生するということは、考えてみれば不思議ではないのですが (たとえば 4.5 を 4 にキャストするには 4 をどこかに作らないといけない) 、上の挙動を知ったときは、なるほどと思いました。

C++ は「こ、これはバッド…」と一瞬思えるような仕様も、Effective C++Stroustrup のC++本 などでよくよく説明を読んでみると意外と納得のいく理由があったりして、なかなかおもしろいです。C++ がなぜ難しいかについては Stroustrup のインタビューをどうぞ。