ゆとりーなの日記

日記的な事を書いて行くと思はれる

const教信者への道

最近C++界隈の新興宗教として「const教」と呼ばれる団体が暗躍してきています。そこで今回は新参Cプラーがこの宗教にのめりこんでいく様子を順を追って解説したいと思います。
まずは「const教」とは無縁の無垢なCプラーのソースコードを見てみましょう。

#include <iostream>

#define DEFAULT_VALUE 0

class Hoge {
 public:
    Hoge();
    ~Hoge();
    void setValue(int value);
    int value();
 private:
    int value_;
};

bool isDefaultValue(Hoge &hoge);

int main() {
    Hoge hoge;
    int input_value;
    std::cin >> input_value;
    int set_value;
    if (input_value) {
        set_value = 1;
    } else {
        set_value = 2;
    }
    hoge.setValue(set_value);
    std::cout << hoge.value() << std::endl;
    return 0;
}

Hoge::Hoge() : value_(DEFAULT_VALUE) {
}

Hoge::~Hoge() {
}

void Hoge::setValue(int value) {
    value_ = value;
}

int Hoge::value() {
    return value_;
}

bool isDefaultValue(Hoge &hoge) {
    return hoge.value() == DEFAULT_VALUE;
}

なんと、constが一つも使われていません。ここからどの様にこのコードを書いた人は「const教」にのめりこんでいくのでしょうか?
まず、C++なのに定数が#defineなのはないわーということにこの人は気づくでしょう。C++においては定数にはconstを使うのが定石だからです。
そこで最初の段階でソースコードは以下の様に変わります。

#include <iostream>

const int kDefaultValue(0);

class Hoge {
 public:
    Hoge();
    ~Hoge();
    void setValue(int value);
    int value();
 private:
    int value_;
};

bool isDefaultValue(Hoge &hoge);

int main() {
    Hoge hoge;
    int input_value;
    std::cin >> input_value;
    int set_value;
    if (input_value) {
        set_value = 1;
    } else {
        set_value = 2;
    }
    hoge.setValue(set_value);
    std::cout << hoge.value() << std::endl;
    return 0;
}

Hoge::Hoge() : value_(kDefaultValue) {
}

Hoge::~Hoge() {
}

void Hoge::setValue(int value) {
    value_ = value;
}

int Hoge::value() {
    return value_;
}

bool isDefaultValue(Hoge &hoge) {
    return hoge.value() == kDefaultValue;
}

次にこの人は、isDefaultValue関数でHogeオブジェクトを参照渡ししていますが、C++においては参照渡しはconst付けた方がいいという噂を聞きます。そこでこの関数を眺めると、オブジェクトは特に何も変わっていないことに気付きconst参照渡しに出来るのではないかと思いだします。よってソースコードは次のように変わります。

#include <iostream>

const int kDefaultValue(0);

class Hoge {
 public:
    Hoge();
    ~Hoge();
    void setValue(int value);
    int value();
 private:
    int value_;
};

bool isDefaultValue(const Hoge &hoge);

int main() {
    Hoge hoge;
    int input_value;
    std::cin >> input_value;
    int set_value;
    if (input_value) {
        set_value = 1;
    } else {
        set_value = 2;
    }
    hoge.setValue(set_value);
    std::cout << hoge.value() << std::endl;
    return 0;
}

Hoge::Hoge() : value_(kDefaultValue) {
}

Hoge::~Hoge() {
}

void Hoge::setValue(int value) {
    value_ = value;
}

int Hoge::value() {
    return value_;
}

bool isDefaultValue(const Hoge &hoge) {
    return hoge.value() == kDefaultValue;
}

ところがこのコードはコンパイルが通らないという現実にぶち当たります。「オブジェクトが変わってないからconstを付けてやったのになんでエラーなんだよ、マジconst使えねー、もう二度と使わねーし」って思った人は幸せなことに、この段階で既に「const教」の魔の手から逃れられたことになります。
ここで少しconstについて勉強した人は、Hoge::valueメソッドがconstなメソッドではないからエラーになるのだということを知ります。そこで、Hoge::valueメソッドの様にオブジェクトの中身を書き変えないメソッドにせっせとconstを付けるようになります。

#include <iostream>

const int kDefaultValue(0);

class Hoge {
 public:
    Hoge();
    ~Hoge();
    void setValue(int value);
    int value() const;
 private:
    int value_;
};

bool isDefaultValue(const Hoge &hoge);

int main() {
    Hoge hoge;
    int input_value;
    std::cin >> input_value;
    int set_value;
    if (input_value) {
        set_value = 1;
    } else {
        set_value = 2;
    }
    hoge.setValue(set_value);
    std::cout << hoge.value() << std::endl;
    return 0;
}

Hoge::Hoge() : value_(kDefaultValue) {
}

Hoge::~Hoge() {
}

void Hoge::setValue(int value) {
    value_ = value;
}

int Hoge::value() const {
    return value_;
}

bool isDefaultValue(const Hoge &hoge) {
    return hoge.value() == kDefaultValue;
}

と、ここまでconstが沢山付きましたが、ここまでで止まる程度では「const教」信者とは言えません。寧ろここまでのconst付けはCプラーの義務と言っても過言ではありません。ここよりも手前で躓いてしまった人は、どちらかと言うとCプラーとしてまずいです。
それは置いといて、何かを悟った真の「const教」信者は、更に色々な所にconstを付け出します。

#include <iostream>

const int kDefaultValue(0);

class Hoge {
 public:
    Hoge();
    ~Hoge();
    void setValue(int value);
    int value() const;
 private:
    int value_;
};

bool isDefaultValue(const Hoge &hoge);

int main() {
    Hoge hoge;
    int input_value;
    std::cin >> input_value;
    const int set_value(input_value ? 1 : 2);
    hoge.setValue(set_value);
    std::cout << hoge.value() << std::endl;
    return 0;
}

Hoge::Hoge() : value_(kDefaultValue) {
}

Hoge::~Hoge() {
}

void Hoge::setValue(int value) {
    value_ = value;
}

int Hoge::value() const {
    return value_;
}

bool isDefaultValue(const Hoge &hoge) {
    return hoge.value() == kDefaultValue;
}

まず、ローカル変数にconstが付きだします。「初期化してあとは書き変えないで参照しかしない変数なら、const付けて良くないですか?」という理論です。constを付けると代入が出来なくなるので自然とif-else節による初期化が減り、条件演算子を使った初期化がメインになってきます。そしてさらに進化した「const教」信者はこの理論を引数についても持ち込みます。

#include <iostream>

const int kDefaultValue(0);

class Hoge {
 public:
    Hoge();
    ~Hoge();
    void setValue(int value);
    int value() const;
 private:
    int value_;
};

bool isDefaultValue(const Hoge &hoge);

int main() {
    Hoge hoge;
    int input_value;
    std::cin >> input_value;
    const int set_value(input_value ? 1 : 2);
    hoge.setValue(set_value);
    std::cout << hoge.value() << std::endl;
    return 0;
}

Hoge::Hoge() : value_(kDefaultValue) {
}

Hoge::~Hoge() {
}

void Hoge::setValue(const int value) {
    value_ = value;
}

int Hoge::value() const {
    return value_;
}

bool isDefaultValue(const Hoge &hoge) {
    return hoge.value() == kDefaultValue;
}

これが「const教」にどっぷり浸かった信者のコードの最終形です。最初のconstが一個もなかったコードと比較してconstが沢山付きました。
どうしてこういう発想のなるのかと言えば、私が思うに答えは簡単で、とあるC++の凄い人のブログでローカル変数にconstが付いていて且つconstを付けることを推奨しているからですね。この人に惹かれた人たちがあらゆる変数に出来る限りconstを付けることに邁進している訳です。まさに宗教という感じがしますね。
勿論私も信者ですよ。