Clang++ 3.5+ 默認開啟優化,即使不指定 `-std=c++11`,也會對函數返回值進行優化。測試代碼:
class A {
public:
A() { cout << "ctor" << endl; }
A(const A&) { cout << "copy" << endl; }
A& operator=(const A&) { cout << "=" << endl; return *this; }
};
A test() {
A a;
cout << &a << endl;
return a;
}
int main() {
A a2 = test();
cout << &a2 << endl;
}
輸出:
ctor 0x7ffd4ed29998 0x7ffd4ed29998
構造函數只調用一次,地址相同 —— 發生了 NRVO (Named Return Value Optimization)。
| 優化類型 | 條件 | C++ 標準 | 備註 |
|---|---|---|---|
| RVO | 返回臨時對象 `return A();` | C++98 | 編譯器自由實現 |
| NRVO | 返回具名變量 `A a; return a;` | C++98 | 需變量類型與返回類型完全一致 |
| Move 語義 | C++11+, 返回右值引用或啟用移動構造 | C++11 | NRVO 失效時的備選 |
| 強制拷貝消除 | C++17 (PRvalue 規則) | C++17 | 強制保證,非優化 |
C++17 將 PRvalue(純右值)的語義改為「初始化器」而非「臨時對象」。對於:
T f() { return T(); } // PRvalue 直接初始化調用者的對象
T x = f(); // 保證無拷貝/移動,x 直接由 f() 的返回表達式初始化
這不再是優化,而是語言強制規定,即使構造函數有副作用也會被消除。
// 錯誤:返回局部對象的引用
T&& bad() { T tmp; return std::move(tmp); } // tmp 析構後引用懸空
// 正確:返回值類型
T good() { T tmp; return tmp; } // NRVO 或 move 構造
// 檢查是否發生拷貝消除
struct Tracker {
Tracker() { puts("ctor"); }
Tracker(const Tracker&) { puts("copy"); }
Tracker(Tracker&&) { puts("move"); }
~Tracker() { puts("dtor"); }
};
Tracker test() { Tracker t; return t; } // 觀察輸出