2024年12月1日 星期日

use of deleted function

這通常是因為在 C++ 中你正在嘗試使用一個被標記為「已刪除」的函數。C++11 引入了「刪除函數(deleted functions)」的概念,這是一種顯式標示某些函數不能使用的機制。

為什麼會出現「use of deleted function」錯誤?

這個錯誤通常出現於以下情況:

  1. 類的拷貝構造函數或拷貝賦值運算符被刪除:這是最常見的情況,當你使用類的拷貝操作時,編譯器會檢查該類是否允許拷貝。如果類的拷貝構造函數或拷貝賦值運算符被顯式標記為 deleted,那麼在代碼中使用拷貝或賦值將會導致此錯誤。

    例如:

    class MyClass {
    public: MyClass(const MyClass&) = delete; // 禁止拷貝構造 MyClass& operator=(const MyClass&) = delete; // 禁止拷貝賦值 };

    如果你嘗試拷貝或賦值該類的對象,編譯器會報錯。

  2. 特殊成員函數被刪除:有些特殊成員函數(如移動構造函數、移動賦值運算符)如果你沒有提供,或者顯式標記為 delete,也可能導致這個錯誤。

  3. 某些 Qt 類型未允許拷貝:例如,某些 Qt 類型(例如 QQmlApplicationEngine)可能會禁用拷貝操作,以防止不當使用。

如何解決「use of deleted function」錯誤?

  1. 檢查是否不小心拷貝了對象: 如果錯誤出現在對某個對象進行拷貝時,並且該類禁用了拷貝構造函數,你應該避免拷貝該對象。可以使用引用或指針來傳遞對象,避免不必要的拷貝。

    例如,假設你有一個類 MyClass,並且拷貝構造函數被刪除,這樣的代碼會報錯:

    MyClass obj1;
    MyClass obj2 = obj1; // 這會報錯,因為拷貝構造函數被刪除

    解決方案是使用引用或指針:

    MyClass obj1;
    MyClass& obj2 = obj1; // 使用引用
  2. 確保正確使用移動語義: 如果你正在使用移動語義,請確保你正確實現了移動構造函數和移動賦值運算符,或者如果不打算使用移動語義,可以將這些函數標記為 delete

    例如,移動構造函數和移動賦值運算符的正確實現:

    class MyClass {
    public: MyClass(MyClass&&) = default; // 開啟移動構造 MyClass& operator=(MyClass&&) = default; // 開啟移動賦值 };
  3. 處理 Qt 類型: 如果這個錯誤來自於使用某個 Qt 類型(例如 QQmlApplicationEngine 或其他),並且這些類型的拷貝構造函數或賦值操作被禁用了,那麼你應該避免直接拷貝這些對象。對於這類情況,可以通過指針或引用來傳遞對象,避免拷貝。

    例如:

    QQmlApplicationEngine engine1;
    QQmlApplicationEngine engine2 = engine1; // 會報錯,因為 QQmlApplicationEngine 禁止拷貝

    解決方案是使用引用:

    QQmlApplicationEngine engine1;
    QQmlApplicationEngine& engine2 = engine1; // 使用引用

    或者使用指針來傳遞:

    QQmlApplicationEngine* engine2 = &engine1; // 使用指針

4. C++ 標準庫中的已刪除函數

除了類的拷貝構造函數或賦值運算符被刪除之外,有些 C++ 標準庫的函數也會被刪除。例如,std::unique_ptr 禁止拷貝或賦值,因為它是為了避免多個指針指向同一個資源而設計的。

std::unique_ptr<int> ptr1 = std::make_unique<int>(10);
std::unique_ptr<int> ptr2 = ptr1; // 這會報錯,因為 unique_ptr 禁止拷貝

解決方法是使用 std::move 進行移動:

std::unique_ptr<int> ptr2 = std::move(ptr1); // 正確,使用移動語義

如果是在 setContextProperty 時發生

通常是因為你正在傳遞的對象類型不允許被拷貝或移動。在 Qt 中,某些類型(例如 QQmlContext)並不支持拷貝或移動操作,因此當你試圖將這些對象傳遞給 setContextProperty 時,可能會出現這個錯誤。

常見原因和解決方案

  1. QQmlContext 無法拷貝QQmlContext 類型本身不支持拷貝或賦值操作,因此你不能將 QQmlContext 或其所包含的對象直接拷貝。

  2. 傳遞不允許拷貝的對象: 如果你將一個不支持拷貝的對象(例如,QQmlApplicationEngineQNetworkAccessManager、或其他不支持拷貝的 Qt 類型)作為參數傳遞給 setContextProperty,則會出現 use of deleted function 的錯誤。

具體解決方案

1. 使用指針或引用來傳遞對象

為了避免拷貝,請使用引用或指針來傳遞對象,這樣不會觸發拷貝操作。這通常是處理此類錯誤的最佳做法。

比如,如果你有一個不支持拷貝的對象,並且想將其暴露給 QML,可以使用指針或引用,而不是直接拷貝對象。

例子:

假設你要將一個 QQmlApplicationEngine 對象暴露到 QML 中,你應該避免直接拷貝 QQmlApplicationEngine,因為它不支持拷貝。

QQmlApplicationEngine engine;
// 使用指針或引用將對象暴露到 QML QQmlContext* context = engine.rootContext(); context->setContextProperty("engine", &engine); // 傳遞指針,避免拷貝

這樣,QML 層就可以通過引用來訪問 QQmlApplicationEngine 對象,而不會產生拷貝錯誤。

2. 避免使用不支持拷貝的對象作為屬性

如果你正在使用 setContextProperty 設置一個 C++ 對象,請確保這個對象本身是可以拷貝的。如果該對象不支持拷貝,則應該避免使用它,或者選擇一個支持拷貝的類型。

如果你的對象需要在 QML 中保持引用,你可以選擇使用指針,並且避免對象的拷貝或移動操作。

3. 檢查傳遞給 setContextProperty 的對象

確保傳遞給 setContextProperty 的對象可以被正常傳遞。如果該對象的類型本身禁止拷貝(例如,它的拷貝構造函數或拷貝賦值運算符被刪除),則應該使用引用或指針來傳遞對象。

完整範例:

#include <QGuiApplication>
#include <QQmlApplicationEngine> #include <QQmlContext> int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; // 創建一些 C++ 物件,這些物件需要被暴露到 QML int someValue = 42; QQmlContext* context = engine.rootContext(); // 使用指針來暴露 C++ 對象給 QML context->setContextProperty("someValue", &someValue); // 傳遞指針 engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }

關鍵點:

  1. 使用指針:在暴露給 QML 時,避免傳遞不支持拷貝的對象,改為使用指針。
  2. 避免拷貝:如果對象不支持拷貝,請選擇引用或指針來傳遞,而不是直接拷貝對象。

沒有留言:

張貼留言