首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

泛型程式設計中的移動建構函數:Mojo 框架消除不必要的臨時物件複製

     編輯:關於C++

問題背景:臨時物件複製的效能代價

C++ 編譯器頻繁建立、複製、銷毀臨時物件。對於擁有動態記憶體的類別(如 String、Matrix、File),複製成本高昂:

vector<string> vec = ReadFile();  // 臨時 vector 複製給 vec
string s1 = s2 + s3;              // operator+ 產生臨時 string 再複製

傳統解法(傳參考、避免運算子)破壞介面直觀性,降低開發效率。

核心洞察:移動而非複製

關鍵在於「偵測臨時物件」並執行破壞性複製——竊取其內部資源指標,將來源重置為空狀態。因臨時物件即將銷毀,只需確保其析構安全即可。

class String {
    char* data_; size_t length_;
public:
    // 傳統複製建構函式:深度複製
    String(const String& rhs) : data_(new char[rhs.length_]), length_(rhs.length_) {
        std::copy(rhs.data_, rhs.data_ + length_, data_);
    }
    // 概念上的移動建構函式(C++98 無法直接表達)
    String(temporary String& rhs) : data_(rhs.data_), length_(rhs.length_) {
        rhs.data_ = nullptr;  // 竊取資源,來源置空
    }
};

Mojo 框架:在 C++98 實現移動語意

Andrei Alexandrescu 提出的 Mojo (Move of Joint Objects) 利用函式多載決議規則,區分左值、常量值、臨時值三類引數。

1. 替身類型區分引數性質

struct ConstantString { const String* obj_; };
struct TemporaryString : ConstantString {};  // 繼承關係關鍵:派生類匹配優先

class String {
public:
    operator ConstantString() const { return {this}; }      // 常量/左值調用
    operator TemporaryString() { return {this}; }           // 非常量臨時值調用
    // ...
};

2. 多載函式捕獲不同引數類型

void Connect(TemporaryString);   // 綁定非常量臨時值
void Connect(ConstantString);    // 綁定常量值(含常量臨時值)
void Connect(String& str) {      // 綁定非常量左值
    Connect(ConstantString(str)); // 轉發給常量版本
}

決議邏輯: 臨時值優先匹配 TemporaryString(派生類),常量值匹配 ConstantString,非常量左值匹配 String&。

3. 通用化框架

namespace mojo {
    template<class T> struct constant { const T* data_; /*...*/ };
    template<class T> struct temporary : private constant<T> { /*...*/ };
    
    template<class T> struct enabled {
        operator temporary<T>() { return temporary<T>(*static_cast<T*>(this)); }
        operator constant<T>() const { return constant<T>(*static_cast<const T*>(this)); }
    protected: enabled() {} ~enabled() {}
    };
}

// 使用者類別繼承 enabled 即獲得 Mojo 能力
class String : public mojo::enabled<String> {
public:
    String(mojo::temporary<String> tmp) { /* 移動建構:竊取 tmp.get() 資源 */ }
    String(mojo::constant<String> con) { /* 一般複製建構 */ }
    // ...
};

函式回傳值優化

引入 fnresult<T> 類型包裝回傳值,利用隱式轉換鏈實現零複製回傳:

namespace mojo {
    template<class T> 
    class fnresult : public T {
    public:
        fnresult(const fnresult& rhs) : T(temporary<T>(const_cast<fnresult&>(rhs))) {}
        explicit fnresult(T& rhs) : T(temporary<T>(rhs)) {}
    };
}

template<class T> struct enabled {
    // ... 前述轉換函式 ...
    operator fnresult<T>() { return fnresult<T>(*static_cast<T*>(this)); }
};

mojo::fnresult<String> MakeString() {
    String result; /*...*/ return result;  // 觸發 operator fnresult
}

String dest = MakeString();  // 路徑:result -> fnresult -> String(fnresult) 移動建構

全程無記憶體配置與複製,且不依賴編譯器 RVO/NRVO 優化。

擴充至組合物件與容器

透過 as_temporary 輔助函式配合 traits 型別特徵,自動將移動語意遞歸應用至成員子物件:

class Widget : public mojo::enabled<Widget> {
    String name_;
public:
    Widget(mojo::temporary<Widget> src) 
        : name_(mojo::as_temporary(src.get().name_)) { /*...*/ }
    // ...
};

若 String 支援 Mojo 則移動,否則退回複製,實現漸進式優化。

應用:mojo_ptr 與可移動容器

mojo_ptr 禁止複製左值/常量,僅允許移動臨時值或顯式 as_temporary:

mojo_ptr ptr1;
mojo_ptr ptr2 = mojo::as_temporary(ptr1);  // 顯式移動轉移擁有權

此模式可建立高效、語意清晰的容器(如 vector<mojo_ptr<Widget>>),支援昂貴複製類型的高效重排。

結語

Mojo 以函式多載與型別替身模擬移動語意,在 C++11 std::move 標準化前提供了可移植、高效的解決方案。雖非 100% 透明,但作為庫層級解決方案極具價值。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved