一、
看cppreference時,發現被隱式刪除的構造函數,查詢做如下記錄:
struct F
{int& ref; // reference memberconst int c; // const member// F::F() is implicitly defined as deleted
};// user declared copy constructor (either user-provided, deleted or defaulted)
// prevents the implicit generation of a default constructor
在 C++ 中,struct F
的默認構造函數F::F()
被隱式刪除,主要與類中包含的引用成員和const 成員有關。以下是具體原因及底層機制的詳細解析:
1. 引用成員(int& ref
)的影響
- 引用的特性:引用必須在初始化時綁定到一個已存在的對象,且無法重新綁定。
- 默認構造函數的行為:默認構造函數不會為成員變量提供任何初始化值。當類中存在引用成員時,默認構造函數無法滿足引用的初始化要求,因此會被隱式刪除。
struct A {int& ref; // 引用成員
};A a; // 錯誤:無法默認構造A,因為ref未初始化
2. const 成員(const int c
)的影響
- const 的特性:const 成員必須在構造時初始化,且之后無法修改。
- 默認構造函數的限制:默認構造函數不會為 const 成員賦初值,導致 const 成員未初始化,因此默認構造函數被隱式刪除。
struct B {const int c; // const成員
};B b; // 錯誤:無法默認構造B,因為c未初始化
3. 兩者共同作用的結果
當類同時包含引用成員和const 成員時,默認構造函數需要同時滿足兩者的初始化要求,但默認構造函數無法提供任何初始化邏輯,因此會被編譯器隱式刪除。
struct F {int& ref; // 引用必須初始化const int c; // const必須初始化
};// 等價于:
struct F {int& ref;const int c;F() = delete; // 編譯器隱式添加
};
4. 如何正確構造此類?
必須顯式定義構造函數,為引用成員和 const 成員提供初始化:
struct F {int& ref;const int c;// 顯式構造函數,必須初始化所有成員F(int& r, int val) : ref(r), c(val) {}
};int main() {int x = 10;F f(x, 42); // 正確:引用綁定到x,const成員初始化為42
}
5. 其他導致默認構造函數被刪除的場景
除了引用和 const 成員外,以下情況也會導致默認構造函數被隱式刪除:
- 包含未默認構造的成員(如自定義類型且無默認構造函數):
struct G {std::string str; // std::string有默認構造函數,允許默認構造 };struct H {G g; // G有默認構造函數,H的默認構造函數有效std::mutex m; // std::mutex無默認構造函數,H的默認構造函數被刪除 };
- 基類或成員的默認構造函數被刪除:
struct Base {Base() = delete; // 顯式刪除默認構造函數 };struct Derived : Base {// Derived的默認構造函數被隱式刪除,因為Base無法默認構造 };
總結
struct F
的默認構造函數被隱式刪除的核心原因是:
- 引用成員
ref
必須在構造時綁定到有效對象; - const 成員
c
必須在構造時初始化;
而默認構造函數無法滿足上述要求,因此編譯器自動將其標記為delete
。若要使用此類,必須顯式定義構造函數以完成成員初始化。
二、
struct G
{G(const G&) {}// G::G() is implicitly defined as deleted
};struct H
{H(const H&) = delete;// H::H() is implicitly defined as deleted
};struct I
{I(const I&) = default;// I::I() is implicitly defined as deleted
};
這些結構體的默認構造函數被刪除的原因各不相同,涉及 C++ 的特殊成員函數生成規則。下面逐一分析:
1.?struct G
struct G {G(const G&) {} // 顯式定義拷貝構造函數// G::G() 被隱式刪除
};
原因:
- 拷貝構造函數的影響:當顯式定義了拷貝構造函數(無論是否為默認
default
或刪除delete
),編譯器不會自動生成默認構造函數。 - 用戶責任:若需要默認構造,必須手動定義:
G() = default; // 顯式保留默認構造
2.?struct H
struct H {H(const H&) = delete; // 顯式刪除拷貝構造函數// H::H() 被隱式刪除
};
原因:
- 拷貝構造函數的刪除不直接影響默認構造:刪除拷貝構造函數本身不會導致默認構造函數被刪除。
- 潛在依賴:若
H
的成員或基類需要拷貝構造(例如std::unique_ptr
),編譯器可能因無法生成拷貝構造而隱式刪除默認構造。但僅從代碼看,這一規則不適用,因此H
的默認構造函數不應該被刪除。 - 結論:題目中的注釋可能有誤,
H::H()
?通常不會被隱式刪除,除非存在其他依賴。
3.?struct I
struct I {I(const I&) = default; // 顯式默認拷貝構造函數// I::I() 被隱式刪除
};
原因:
- 與
G
相同的規則:顯式默認=default
拷貝構造函數,仍屬于顯式定義拷貝構造函數,因此編譯器不會自動生成默認構造函數。 - 例外情況:若
I
包含引用成員或const 成員,即使不定義拷貝構造函數,默認構造函數也會被刪除(見前一個問題的解釋)。但題目中未提及這些成員,因此主要原因仍是顯式定義拷貝構造函數。
總結
結構體 | 默認構造函數被刪除的原因 |
---|---|
G | 顯式定義拷貝構造函數,編譯器不生成默認構造。 |
H | 注釋可能有誤,刪除拷貝構造函數本身不影響默認構造。 |
I | 顯式默認拷貝構造函數,編譯器不生成默認構造。 |
C++ 特殊成員函數生成規則
- 默認構造函數:當且僅當沒有顯式定義任何構造函數時,編譯器自動生成。
- 拷貝構造函數:當且僅當沒有顯式定義拷貝 / 移動構造函數時,編譯器自動生成。
- 拷貝賦值運算符:當且僅當沒有顯式定義拷貝 / 移動賦值運算符時,編譯器自動生成。
現代 C++ 建議:
若需要默認構造函數,顯式聲明:
MyClass() = default; // 顯式保留默認行為
若不需要默認構造,顯式刪除:
MyClass() = delete; // 禁止默認構造