引数1個のコンストラクタの暗黙呼び出しとexplicit

サンプルコード explicit.cpp
#include <iostream>
using namespace std;

class C {
protected:
    int x;
public:
    C(int arg) : x(arg) {
        cout << "C::C(int)" << endl;
    }
    C& operator=(int arg) {
        cout << "C::operator=(int)" << endl;
        x = arg;
        return *this;
    }
};

int main(void) {
    C obj = 10;
    return 0;
}

引数をひとつだけとるコンストラクタのことを,“変換コンストラクタ(converting constructor)”と呼ぶ.

引数をひとつだけとるコンストラクタについては,明示的呼び出しと暗黙的呼び出しの2つの呼び出し方がある. たとえば,クラスCに1個の引数をとるコンストラクタC::C(int)が定義されているとすると, 明示的呼び出しと暗黙的呼び出しはそれぞれ次のように記述できる. [C::C(int)のように本当に引数が1個だけのコンストラクタ以外に, C::C(const char*, int = 0)のようにデフォルト引数が指定されているために C obj("cstr");のように引数1個だけを与えて呼び出せるコンストラクタも変換コンストラクタになる.]

呼び出しの種類 コード記述例
明示的呼び出し C obj(10);
暗黙的呼び出し C obj = 10;

C++では,1個の引数をとるコンストラクタC::C(int)を定義した場合にのみ“C obj = 10;”のような記述が許される (引数を1個とるコンストラクタが定義されていない場合,コンパイル時エラーになる). よくある間違いとしては,C obj = 10;のように記述すると,C::operator=(int)が呼び出されるのではないかという誤解があるが, 実際にはC obj = 10;はオブジェクトの構築を意味する文であって代入文(代入演算子の呼び出し)ではないため, 代入演算子であるC::operator=(int)は関係がない. [注:C言語以来,変数の初期化構文と代入文に同じ記号を使う伝統からこのような誤解が生じやすい. しかし,冷静に考えてみれば,同じ“=”という記号を用いるといっても, int a[] = {1, 2, 3};char str[] = "string";のように,初期化のときだけに使える文法があり, 変数初期化の意味の“=”と代入の意味の“=”は明確に違うものとわかる.]

explicit

引数を1個とるコンストラクタの暗黙呼び出しを禁止するには,コンストラクタを“explicit”と宣言しておく. explicit宣言したコンストラクタは,明示的呼び出し(C obj(10);)でしか呼び出せなくなり, 暗黙呼び出しを記述するとコンパイル時エラーになる.

class C {
    ...
    explicit C(int arg) {
       ...
    }

まとめ



はたいたかし
2007-01-12
トップ > 開発ツール > C++