C++の初期化構文(これは古い日記です)

C++では、クラスメンバの初期化に特殊な構文を用いる。

// Foo.h
class Foo {
public:
    Foo::Foo(int x, int y);
private:
    int x_;
    int y_;
};
// Foo.cpp
Foo::Foo(int x, int y) : x_(x), y_(y)
{
}

コンストラクタでx_をxに、y_をyに初期化している。これは、

Foo::Foo(int x, int y)
{
    x_ = x;
    y_ = y;
}

と書いても一見同じように見えるが、前者と後者では

int x = 99;

int x;
x = 99;

くらいに違う。実際intのような組み込み型ではどちらもほとんど変わらないが、クラス型の場合では、両者のセマンティクスは大きく異なる。

Bar bar = 99;  // 普通は Bar bar(99); と書くが

この場合、barはコピーコンストラクタで初期化されるが、

Bar bar;
bar = 99;

この場合は、barは引数なしのデフォルトコンストラクタで初期化された後、2行目でBar::operator=()によって代入が行われている。従って、

Foo::Foo(const Bar& x, const Bar& y)
{
    x_ = x;
    y_ = y;
}

のような書き方では、Barがデフォルトコンストラクタを持っている必要があるし、関数呼び出しが多い分効率が悪い。そのため、

Foo::Foo(const Bar& x, const Bar& y) : x_(x), y_(y)
{
}

のような書き方が正しい。ついでにいえば、基底クラスのコンストラクタに引数を渡す必要がある場合には、この方法でしか基底クラスの初期化は行えない。

まあこれはC++の常識なので、C++を知っている人には改めて説明するまでのことではない。

問題はこの構文だ。この構文は一般的には、

Foo::Foo(int a, int b, int c) : a_(a), b_(b), c_(c)
{
}

のように説明されているが、実際はこんな単純にはいかない。

初期化する変数が多くなると、

Foo::Foo() : normal_value(0), super_value(1), ultra_value(2), miracle_value(3)
{
}

のような状態になってくる。これ以上は一行に収めるのは苦しい。そして、

Foo::Foo()
  : normal_value(0),
    super_value(1),
    ultra_value(2),
    miracle_value(3),
    special_value(4),
    excellent_value(5),
    brilliant_value(6),
    another_value(7),
    the_other_value(8),
    yet_another_value(9)
{
}

こうなる。関数定義の頭の部分だけやたら重くて、中身はたいてい空だ。まあなんというか、見た目が最悪だ。いやまあ、見た目の問題だけなんですが。

C++である程度以上の規模のプログラムを組んでいると、必ずこういうのがあちこちに現れてくる。こうなるのは俺だけなのか?これは設計が悪いんですか?まあ長々と書いて言いたかったことは、C++のこの構文が嫌いです、ということだけなんですが。これに不満を持ってる人は他にいないのだろうか?Web上でこれについて触れられているのを見た記憶がないのだが。やっぱり俺の設計が駄目駄目なんですか。それとも誰もこんな些細なことは気にしないのか。そもそもこの構文の存在を知らない人が大多数だったりしそうで嫌だが…。

ちなみにJavaでは、

class Foo {
    private Bar x;
    private Bar y;
    Foo(Bar x, Bar y) {
        this.x = x;
        this.y = y;
    }
}

と書けば、正しい初期化の意味になる。#JavaC++より優れている部分はこれだけだ。