C++プログラミングにおいて、ソースコードをコンパイルした際に「error: use of deleted function ‘X::X()’」というエラーメッセージに遭遇することは珍しくありません。

このエラーは、プログラム内で呼び出そうとした関数(主にコンストラクタ)が、コンパイラによって「削除された」と判定されていることを示しています。

一見すると、定義したはずのクラスがなぜ使えないのか、あるいはなぜ勝手に関数が削除されてしまうのかと困惑する方も多いでしょう。

しかし、このメッセージはC++の型安全性を守るための重要な警告であり、発生原因を正しく理解すれば解決は難しくありません。

本記事では、このエラーが発生するメカニズムを詳しく紐解き、具体的なコード例を交えながら原因と解決策を詳しく提示していきます。

「use of deleted function」エラーの本質とは

C++における「deleted function(削除された関数)」とは、= delete構文によって明示的に使用が禁止された関数、あるいは言語の規則によって自動的に生成が停止された関数を指します。

コンパイラは、安全性が保証されない操作や、論理的に矛盾が生じる操作を未然に防ぐために、特定の特殊メンバ関数を削除済みとしてマークします。

「use of deleted function ‘X::X()’」というエラーは、まさにその削除されたデフォルトコンストラクタをインスタンス化の際に呼び出そうとしたことによって発生します。

このエラーを解消するためには、なぜコンパイラがその関数を「削除」したのかという理由を特定する必要があります。

原因1:他のコンストラクタの定義による自動生成の停止

C++のクラス設計において最も頻繁に発生するのが、引数を持つコンストラクタを定義したことで、デフォルトコンストラクタの自動生成が止まってしまうケースです。

C++の仕様では、開発者が一つでもコンストラクタを明示的に定義すると、コンパイラは「このクラスには特別な初期化手順が必要である」と判断します。

その結果、引数を持たないデフォルトコンストラクタ X::X() は自動的に生成されなくなります。

暗黙のデフォルトコンストラクタが消失する例

以下のコードでは、引数付きのコンストラクタを定義したためにエラーが発生します。

C++
#include <iostream>
#include <string>

class User {
public:
    // 引数付きのコンストラクタを定義
    User(std::string name) : name_(name) {}

private:
    std::string name_;
};

int main() {
    // 引数なしでインスタンス化を試みる(ここでエラー発生)
    User u; 
    return 0;
}
実行結果
error: use of deleted function 'User::User()'
note: 'User::User()' is implicitly deleted because 'User' has a user-declared constructor

この問題の解決策

この場合の解決策は非常に単純で、デフォルトコンストラクタを明示的に定義するか、= default を使用してコンパイラに生成を依頼することです。

C++11以降では、= default を用いることで、コンパイラによる標準的な実装を再利用できます。

C++
class User {
public:
    // デフォルトコンストラクタを明示的に要求
    User() = default; 
    User(std::string name) : name_(name) {}

private:
    std::string name_ = "unknown"; // デフォルト値を指定しておくと安全
};

原因2:メンバ変数に初期化不可能な型が含まれている

クラスのメンバ変数に、「デフォルトコンストラクトできない型」が含まれている場合、そのクラス自身のデフォルトコンストラクタも暗黙的に削除されます。

代表的な例としては、参照型の変数(reference members)や、const指定された変数(const members)が挙げられます。

これらは宣言と同時に初期化する必要があるため、引数のないデフォルトコンストラクタでは初期化のしようがないためです。

参照型やconst型を含むクラスの例

C++
class Database {
public:
    // 何も定義しない場合、デフォルトコンストラクタが自動生成されるはずだが...
private:
    int& connection_id; // 参照型は初期化が必須
    const int version;  // const型も初期化が必須
};

int main() {
    Database db; // エラー:デフォルトコンストラクタが削除されている
    return 0;
}

この問題の解決策

メンバ初期化リストを使用して、全てのメンバ変数を確実に初期化するようにコンストラクタを定義します。

あるいは、メンバ変数の宣言時にデフォルト値を直接指定(In-class member initializers)することも有効です。

C++
class Database {
public:
    // メンバ初期化リストで値を確定させる
    Database(int& id, int v) : connection_id(id), version(v) {}

private:
    int& connection_id;
    const int version;
};

原因3:コピー禁止・ムーブ専用型をメンバに持っている

現代のC++開発において非常に多いのが、std::unique_ptrstd::mutex といった「コピー不可能な型」をクラスのメンバに持たせているケースです。

これらの型自体はデフォルトコンストラクタを持っていることが多いですが、コピーコンストラクタが削除されています。

この状況で、クラスを意図せずコピーしようとすると、「use of deleted function ‘X::X(const X&)’」という類のエラーが発生します。

今回取り上げているデフォルトコンストラクタのエラーにおいても、これらのメンバを持つクラスのコンストラクタ挙動が複雑化し、エラーの引き金になることがあります。

削除される条件の比較

要因デフォルトコンストラクタへの影響主な解決策
自作コンストラクタの存在自動生成が停止する= default を追記する
参照型メンバ暗黙的に削除される初期化リストで値を渡す
constメンバ(初期値なし)暗黙的に削除される宣言時に初期化するか、コンストラクタで初期化
親クラスのコンストラクタが削除済み継承先でも削除される親クラスの設計を見直す

原因4:継承関係における基本クラスの制約

クラスの継承を行っている場合、派生クラスのコンストラクタは暗黙的に基本クラス(親クラス)のデフォルトコンストラクタを呼び出そうとします。

もし、基本クラスのデフォルトコンストラクタが削除されていたり、アクセス不可(private)であったりする場合、派生クラスのデフォルトコンストラクタも自動的に削除されます。

これは、オブジェクトの生成過程において「土台となる親の部分」を正しく構築できないためです。

継承によってエラーが伝播する例

C++
class Base {
public:
    // 引数付きのみ。デフォルトコンストラクタは生成されない。
    Base(int val) {}
};

class Derived : public Base {
    // コンパイラは Derived::Derived() を生成しようとするが、
    // Base::Base() が見つからないため、Derived::Derived() を削除済みにする。
};

int main() {
    Derived d; // error: use of deleted function 'Derived::Derived()'
    return 0;
}

この問題の解決策

派生クラスのコンストラクタにおいて、親クラスの既存のコンストラクタを明示的に呼び出すように記述します。

あるいは、親クラス側にデフォルトコンストラクタを定義します。

C++
class Derived : public Base {
public:
    // 親クラスの引数付きコンストラクタに値を渡して初期化する
    Derived() : Base(0) {} 
};

高度なケース:std::variant や std::optional との組み合わせ

モダンなC++開発において、std::variant を使用する際にこのエラーに直面することがあります。

std::variant の最初の型がデフォルトコンストラクト可能でない場合、std::variant 自体のデフォルトコンストラクタも削除されます。

これは、variantがインスタンス化された瞬間に「どの型で初期化されるべきか」が確定できないためです。

このような場合は、std::monostate を最初の候補に含めることで、空の状態をデフォルトとして許容する解決策が一般的です。

C++
#include <variant>

struct NoDefault {
    NoDefault(int x) {}
};

int main() {
    // std::variant<NoDefault, int> v; // これはエラーになる
    std::variant<std::monostate, NoDefault, int> v; // 成功
    return 0;
}

エラーを未然に防ぐためのベストプラクティス

「use of deleted function」エラーを回避し、メンテナンス性の高いコードを書くためには、以下のルールを意識することが推奨されます。

Rule of Zero(ゼロの法則)の活用

可能な限り、カスタムコンストラクタやデストラクタを自分で定義せず、標準ライブラリのコンポーネントに管理を任せる設計を目指します。

これにより、コンパイラが最適な特殊メンバ関数を自動生成し、削除エラーのリスクを最小限に抑えることができます。

メンバ変数の初期値を宣言時に指定する

C++11以降で利用可能な「非静的データメンバ初期化」を積極的に利用しましょう。

クラスの宣言内で int value = 0; のように初期値を指定しておくことで、複数のコンストラクタを定義した際の初期化漏れを防ぐことができます。

コンパイラのメッセージを細部まで読む

近年のコンパイラ(GCC 13+ や Clang 16+)は、非常に親切なエラーメッセージを出力します。

「なぜ削除されたのか(because …)」という理由が必ず出力されているため、その原因となったメンバ変数や親クラスを特定することが解決への近道です。

まとめ

C++における「use of deleted function ‘X::X()’」エラーは、クラスの構築に必要な条件が満たされていないことをコンパイラが教えてくれる親切な通知です。

その主な原因は、引数付きコンストラクタの定義による自動生成の停止、初期化不可能なメンバ(参照やconst)の存在、あるいは継承元の制約に集約されます。

エラーが発生した際は、まず「そのクラスがどのように初期化されるべきか」という設計意図を再確認してください。

= default による明示的な生成や、メンバ初期化リストの適切な使用、そしてモダンな std::monostate などのテクニックを組み合わせることで、このエラーは確実に解消できます。

この記事で紹介したパターンと解決策を参考に、堅牢でクリーンなC++コードの記述を目指してください。