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.cppexplicit.makexplicit
引数を1個とるコンストラクタの暗黙呼び出しを禁止するには,コンストラクタを“explicit”と宣言しておく.
explicit宣言したコンストラクタは,明示的呼び出し(C obj(10);)でしか呼び出せなくなり,
暗黙呼び出しを記述するとコンパイル時エラーになる.
class C {
...
explicit C(int arg) {
...
}
C obj = 10;”は“C obj(10);”に変換される(等価である).
C::operator=(int)が呼び出されるのではない.C::operator=(int)が定義してあっても・なくても同様.
C obj = 10;はオブジェクトの構築なので,代入文とは明確に区別され,必ずC obj(10);の意味に解釈される.
[そういえば,“C obj = C(10);”という書き方もできる.
この書き方は“関数記法型変換(functional notation type conversion)”という.]
explicit C(int)と宣言する.