クラスの初期化やコンストラクタ呼び出しを行った際に、次のようなエラーが発生する。
struct T {};
T t(9); // error: No matching constructor for initialization of 'T'
T(8); // error: No matching conversion for functional-style cast from 'int' to 'T'
T u = 7; // error: No viable conversion from 'int' to 'T'
これらのエラーの原因は、コンストラクタの呼び出しが行えていないことである。
呼び出し対象のコンストラクタをきちんと定義する必要がある。
struct T {
T(int i) {} // int型の値を受け取るコンストラクタを定義する
};
T t(9); // ok
T(8); // ok
T u = 7; // ok
他にも様々なケースによって今回のようなエラーが発生する。
目次
- コンストラクタの暗黙的定義が行われないケース
- 初期化子リストを定義した場合
- Call to deleted constructor エラーについて
- Calling a private constructor エラーについて
コンストラクタの暗黙的定義が行われないケース
ちなみにC++では、コンストラクタが暗黙的に未定義となるケースがあり、この場合も同様のエラーが発生する。
struct T { T(int i) {} };
T(); // error: No matching constructor for initialization of 'T'
C++には、引数を取るコンストラクタを独自に定義した場合に、デフォルトコンストラクタの暗黙的な定義が行われなくなるという特性がある。その状態でデフォルトコンストラクタを呼び出した場合には、やはりエラーが発生することとなる。
対策としては、コンストラクタの明示的な定義を行う必要がある。
struct T {
T(int i) {}
T() {} // 明示的に定義する必要がある
};
T(); // ok
または、変換コンストラクタに対してデフォルト引数を指定することも可能である。コンストラクタ仮引数の全てにデフォルト引数を指定することによって、実質的にデフォルトコンストラクタとしても機能するようになる。
struct T {
T(int i = 0) {} // デフォルトコンストラクタとしても機能
};
T(); // ok
参考:変換コンストラクタ #デフォルトコンストラクタとして機能させる方法
初期化子リストを定義した場合
初期化子リスト(std::initializer_list
)を受け取るコンストラクタを定義した場合にも同様のエラーが発生する場合がある。この場合は波括弧({}
)を用いた初期化が必要となる。
struct T { T(std::initializer_list<int> list) {} };
T(1, 2); // error: No matching constructor for initialization of 'T'
T{1, 2}; // ok
T({1, 2}); // ok
どうしても丸括弧を用いた形式でコンストラクタの初期化を行いたい場合には、初期化子リストコンストラクタとは別に、対応するコンストラクタを多重定義する必要がある。
struct T {
T(std::initializer_list<int> list) {}
T(int a, int b) {} // 必要なコンストラクタを多重定義する
};
T(1, 2); // `T(int a, int b)`が呼ばれる
T{1, 2}; // `T(std::initializer_list<int> list)`が呼ばれる
参考:複数の引数を取る変換コンストラクタ
Call to deleted constructor エラーについて
コンストラクタのデリート定義(T() = delete;
)を行った場合も同様に、コンストラクタが未定義の状態となるため、それ相応のエラーが発生する。
struct T { T() = delete; };
T(); // error: Call to deleted constructor of 'T'
この場合の対応としては、デフォルト定義(T() = default;
)またはコンストラクタの明示的な定義が必要となる。
struct T {
T() = default; // 明示的なデフォルト定義
T(int i) {} // 明示的なコンストラクタ定義
};
T(); // ok
T(9); // ok
もっとも、デリート定義を行うということは、それ相応の設計理由があるためである。既存のデリート定義の解除は慎重に行い、また無闇に解除しないようにしたい。
Calling a private constructor エラーについて
コンストラクタがprivate(非公開)の状態で宣言された場合では、また異なるエラーが発生する。
原因は、class
キーワードによるクラス宣言やprivate
アクセス指定子によるコンストラクタ宣言によって、対象のコンストラクタにアクセスできなくなっていることである。
/* classキーワードの特性によりデフォルトでprivate指定される */
class T { T() {} };
T(); // error: Calling a private constructor of class 'T'
/* privateアクセス指定子の影響によってアクセス出来なくなる */
struct U { private: U() {} };
U(); // error: Calling a private constructor of class 'U'
対応としては、コンストラクタをきちんとpublic(公開)な状態で宣言する必要がある。
class T {
T(int i) {}
public:
T() {}
};
T(); // ok
T(9); // error(デフォルトでprivate指定されているため)
ちなみに、struct
キーワードによるクラス宣言を行えば、デフォルトでpublicによるアクセス指定が適用されるため、明示的なアクセス指定を省略することも可能となる。
struct T {
T(int i) {}
T() {}
};
T(); // ok
T(9); // ok