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; // 竊取資源,來源置空
}
};
Andrei Alexandrescu 提出的 Mojo (Move of Joint Objects) 利用函式多載決議規則,區分左值、常量值、臨時值三類引數。
struct ConstantString { const String* obj_; };
struct TemporaryString : ConstantString {}; // 繼承關係關鍵:派生類匹配優先
class String {
public:
operator ConstantString() const { return {this}; } // 常量/左值調用
operator TemporaryString() { return {this}; } // 非常量臨時值調用
// ...
};
void Connect(TemporaryString); // 綁定非常量臨時值
void Connect(ConstantString); // 綁定常量值(含常量臨時值)
void Connect(String& str) { // 綁定非常量左值
Connect(ConstantString(str)); // 轉發給常量版本
}
決議邏輯: 臨時值優先匹配 TemporaryString(派生類),常量值匹配 ConstantString,非常量左值匹配 String&。
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 禁止複製左值/常量,僅允許移動臨時值或顯式 as_temporary:
mojo_ptr ptr1;
mojo_ptr ptr2 = mojo::as_temporary(ptr1); // 顯式移動轉移擁有權
此模式可建立高效、語意清晰的容器(如 vector<mojo_ptr<Widget>>),支援昂貴複製類型的高效重排。
Mojo 以函式多載與型別替身模擬移動語意,在 C++11 std::move 標準化前提供了可移植、高效的解決方案。雖非 100% 透明,但作為庫層級解決方案極具價值。