시리즈: C++
cpp
422 줄
· 업데이트 2026-04-03
perfect_forwarding.cpp
C++/Part4_現代CPP/Ch17_移動語意/perfect_forwarding.cpp
// ============================================================
// Ch17 — 完美轉發(Perfect Forwarding)
// 編譯:g++ -std=c++17 -Wall -o perfect_forwarding perfect_forwarding.cpp
// ============================================================
#include <iostream>
#include <string>
#include <memory>
#include <utility>
#include <vector>
#include <chrono>
#include <functional>
// ── 輔助函式 ──
void section(const std::string& title) {
std::cout << "\n========================================\n";
std::cout << " " << title << "\n";
std::cout << "========================================\n";
}
// ============================================================
// 1. 值類別偵測
// ============================================================
void process(int& x) {
std::cout << " process(int&) — 收到左值: " << x << "\n";
}
void process(int&& x) {
std::cout << " process(int&&) — 收到右值: " << x << "\n";
}
void process(const std::string& s) {
std::cout << " process(const string&) — 收到左值: \"" << s << "\"\n";
}
void process(std::string&& s) {
std::cout << " process(string&&) — 收到右值: \"" << s << "\"\n";
}
// ============================================================
// 2. 不完美的包裝函式
// ============================================================
// 版本 A:所有引數都變成左值(即使原本是右值)
template<typename T>
void bad_wrapper(T arg) {
std::cout << " bad_wrapper 呼叫 process:\n ";
process(arg); // arg 永遠是左值!
}
// ============================================================
// 3. 完美轉發的包裝函式
// ============================================================
template<typename T>
void good_wrapper(T&& arg) {
std::cout << " good_wrapper 呼叫 process:\n ";
process(std::forward<T>(arg)); // 保留原始值類別
}
// ============================================================
// 4. 通用日誌包裝器
// ============================================================
template<typename Func, typename... Args>
decltype(auto) logAndCall(const std::string& funcName,
Func&& func,
Args&&... args) {
std::cout << " [LOG] 呼叫 " << funcName << "\n";
auto start = std::chrono::high_resolution_clock::now();
decltype(auto) result = std::forward<Func>(func)(
std::forward<Args>(args)...
);
auto end = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
std::cout << " [LOG] " << funcName << " 完成,耗時 " << elapsed << " μs\n";
return result;
}
// 為 void 回傳值提供特化版本
template<typename Func, typename... Args>
void logAndCallVoid(const std::string& funcName,
Func&& func,
Args&&... args) {
std::cout << " [LOG] 呼叫 " << funcName << "\n";
std::forward<Func>(func)(std::forward<Args>(args)...);
std::cout << " [LOG] " << funcName << " 完成\n";
}
// ============================================================
// 5. 工廠函式模式
// ============================================================
class Widget {
public:
Widget() : id_(0), name_("default") {
std::cout << " Widget() 預設建構\n";
}
Widget(int id, const std::string& name) : id_(id), name_(name) {
std::cout << " Widget(" << id_ << ", \"" << name_ << "\") 建構\n";
}
Widget(int id, std::string&& name) : id_(id), name_(std::move(name)) {
std::cout << " Widget(" << id_ << ", \"" << name_ << "\") 建構(name 被移動)\n";
}
void info() const {
std::cout << " Widget #" << id_ << ": " << name_ << "\n";
}
~Widget() {
std::cout << " Widget(" << id_ << ") 解構\n";
}
private:
int id_;
std::string name_;
};
// 自訂 make_unique — 使用完美轉發
template<typename T, typename... Args>
std::unique_ptr<T> my_make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
// ============================================================
// 6. emplace_back vs push_back
// ============================================================
class HeavyObject {
public:
HeavyObject(int id, const std::string& data)
: id_(id), data_(data) {
std::cout << " HeavyObject(" << id_ << ") 建構\n";
}
HeavyObject(const HeavyObject& other)
: id_(other.id_), data_(other.data_) {
std::cout << " HeavyObject(" << id_ << ") 複製建構\n";
}
HeavyObject(HeavyObject&& other) noexcept
: id_(other.id_), data_(std::move(other.data_)) {
std::cout << " HeavyObject(" << id_ << ") 移動建構\n";
}
~HeavyObject() {
std::cout << " HeavyObject(" << id_ << ") 解構\n";
}
int id() const { return id_; }
private:
int id_;
std::string data_;
};
// ============================================================
// 7. 引用摺疊規則示範
// ============================================================
template<typename T>
std::string describeType(T&&) {
if constexpr (std::is_lvalue_reference_v<T>) {
return "T 被推導為左值參考(T&)";
} else {
return "T 被推導為非參考型別(T)";
}
}
// ============================================================
// 8. 條件式移動
// ============================================================
template<typename T>
void conditionalProcess(T&& arg, bool shouldMove) {
if (shouldMove) {
process(std::forward<T>(arg));
} else {
// 強制以左值處理
auto& lref = arg;
process(lref);
}
}
int main() {
std::cout << "===== Ch17:完美轉發示範 =====\n";
// ──────────────────────────────────────────
// 1. 轉發參考基礎
// ──────────────────────────────────────────
section("1. 轉發參考(T&&)");
{
int x = 42;
std::cout << "傳入左值 x:\n";
std::cout << " " << describeType(x) << "\n";
std::cout << "傳入右值 42:\n";
std::cout << " " << describeType(42) << "\n";
std::cout << "傳入 std::move(x):\n";
std::cout << " " << describeType(std::move(x)) << "\n";
std::cout << "\n引用摺疊規則:\n";
std::cout << " T& & → T& (左值參考的左值參考 → 左值參考)\n";
std::cout << " T& && → T& (左值參考的右值參考 → 左值參考)\n";
std::cout << " T&& & → T& (右值參考的左值參考 → 左值參考)\n";
std::cout << " T&& && → T&& (右值參考的右值參考 → 右值參考)\n";
}
// ──────────────────────────────────────────
// 2. 不完美轉發的問題
// ──────────────────────────────────────────
section("2. 不完美轉發 vs 完美轉發");
{
int x = 10;
std::cout << "--- 直接呼叫 ---\n";
std::cout << "process(x)(左值):\n ";
process(x);
std::cout << "process(42)(右值):\n ";
process(42);
std::cout << "\n--- 不完美的包裝(bad_wrapper) ---\n";
std::cout << "bad_wrapper(x):\n";
bad_wrapper(x); // arg 是副本,永遠是左值
std::cout << "bad_wrapper(42):\n";
bad_wrapper(42); // 右值變成了左值!
std::cout << "\n--- 完美轉發(good_wrapper) ---\n";
std::cout << "good_wrapper(x):\n";
good_wrapper(x); // 保持左值
std::cout << "good_wrapper(42):\n";
good_wrapper(42); // 保持右值 ✓
}
// ──────────────────────────────────────────
// 3. std::forward 如何運作
// ──────────────────────────────────────────
section("3. std::forward 的原理");
{
std::cout << R"(
template<typename T>
void wrapper(T&& arg) {
// 當傳入左值 int x:
// T 推導為 int&
// arg 的型別是 int& && → int&(引用摺疊)
// forward<int&>(arg) → 回傳 int&(左值)
//
// 當傳入右值 42:
// T 推導為 int
// arg 的型別是 int&&
// forward<int>(arg) → 回傳 int&&(右值)
target(std::forward<T>(arg));
}
)";
std::cout << "→ std::forward 根據 T 的推導結果決定轉發為左值或右值\n";
}
// ──────────────────────────────────────────
// 4. 完美轉發的日誌包裝器
// ──────────────────────────────────────────
section("4. 通用日誌包裝器");
{
// 包裝計算函式
auto add = [](int a, int b) { return a + b; };
int result = logAndCall("add", add, 3, 4);
std::cout << " 結果:" << result << "\n\n";
// 包裝字串處理
auto concat = [](const std::string& a, const std::string& b) {
return a + b;
};
std::string s = logAndCall("concat", concat,
std::string("Hello"), std::string(" World"));
std::cout << " 結果:\"" << s << "\"\n\n";
// 包裝 void 函式
auto greet = [](const std::string& name) {
std::cout << " 你好," << name << "!\n";
};
logAndCallVoid("greet", greet, std::string("C++ 學習者"));
}
// ──────────────────────────────────────────
// 5. 工廠函式模式
// ──────────────────────────────────────────
section("5. 工廠函式(my_make_unique)");
{
// 傳入左值
std::string name = "Alpha";
std::cout << "--- 傳入左值 name ---\n";
auto w1 = my_make_unique<Widget>(1, name);
w1->info();
std::cout << "name 仍然有效:\"" << name << "\"\n";
// 傳入右值
std::cout << "\n--- 傳入右值 ---\n";
auto w2 = my_make_unique<Widget>(2, std::string("Beta"));
w2->info();
// 預設建構
std::cout << "\n--- 無參數 ---\n";
auto w3 = my_make_unique<Widget>();
w3->info();
std::cout << "\n--- 物件銷毀 ---\n";
}
// ──────────────────────────────────────────
// 6. emplace_back vs push_back
// ──────────────────────────────────────────
section("6. emplace_back vs push_back");
{
std::vector<HeavyObject> vec;
vec.reserve(4); // 預留空間避免擴容
// push_back 臨時物件 — 建構 + 移動
std::cout << "--- push_back(HeavyObject{...}) ---\n";
vec.push_back(HeavyObject{1, "Data-1"});
// emplace_back — 直接在容器內建構(完美轉發參數)
std::cout << "\n--- emplace_back(2, \"Data-2\") ---\n";
vec.emplace_back(2, "Data-2");
// push_back 具名物件 — 複製
std::cout << "\n--- push_back(existing) ---\n";
HeavyObject obj(3, "Data-3");
vec.push_back(obj);
// emplace_back 本質上等於:
// new (&storage) T(std::forward<Args>(args)...);
std::cout << "\nemplace_back 的優勢:\n";
std::cout << " - 避免建構臨時物件再移動/複製\n";
std::cout << " - 直接在容器的記憶體中就地建構\n";
std::cout << " - 底層使用完美轉發\n";
std::cout << "\n--- 容器銷毀 ---\n";
}
// ──────────────────────────────────────────
// 7. 多參數完美轉發
// ──────────────────────────────────────────
section("7. 可變參數模板 + 完美轉發");
{
// 通用的 construct 函式
auto construct = [](auto&&... args) {
return Widget(std::forward<decltype(args)>(args)...);
};
std::cout << "--- 用不同參數建構 Widget ---\n";
auto w1 = construct();
w1.info();
std::string name = "Constructed";
auto w2 = construct(42, name);
w2.info();
auto w3 = construct(99, std::string("RvalueConstructed"));
w3.info();
std::cout << "\n--- 離開作用域 ---\n";
}
// ──────────────────────────────────────────
// 8. 完美轉發的常見模式
// ──────────────────────────────────────────
section("8. 完美轉發常見模式總結");
{
std::cout << "1. 工廠函式:\n";
std::cout << R"( template<typename T, typename... Args>
auto create(Args&&... args) {
return T(std::forward<Args>(args)...);
}
)";
std::cout << "\n2. 包裝函式 / 代理(Proxy):\n";
std::cout << R"( template<typename Func, typename... Args>
decltype(auto) wrap(Func&& f, Args&&... args) {
// 前置處理...
decltype(auto) result = std::forward<Func>(f)(
std::forward<Args>(args)...
);
// 後置處理...
return result;
}
)";
std::cout << "\n3. 容器的 emplace 系列函式:\n";
std::cout << R"( template<typename... Args>
void emplace_back(Args&&... args) {
new (storage) T(std::forward<Args>(args)...);
}
)";
std::cout << "\n4. 建構子轉發:\n";
std::cout << R"( template<typename... Args>
explicit Wrapper(Args&&... args)
: inner_(std::forward<Args>(args)...) {}
)";
}
// ──────────────────────────────────────────
// 9. 注意事項
// ──────────────────────────────────────────
section("9. 注意事項");
{
std::cout << "1. std::forward 只能用在轉發參考(T&&)的情境\n";
std::cout << "2. 不要對同一個轉發參考呼叫兩次 std::forward\n";
std::cout << " (第二次可能存取到已移動的物件)\n";
std::cout << "3. auto&& 也是轉發參考:\n";
std::cout << R"(
auto&& x = someFunc(); // x 的型別取決於 someFunc 的回傳值
for (auto&& elem : container) { ... } // 最泛化的 range-for
)";
std::cout << "4. 轉發參考只出現在 template<typename T> 的 T&& 中\n";
std::cout << " Widget&& 不是轉發參考,它就是右值參考\n";
}
std::cout << "\n===== 完美轉發示範結束 =====\n";
return 0;
}
관련 글
C++
c
업데이트 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/deviceAlpha.h).
글 읽기 →
C++
c
업데이트 2026-07-21
finalproject.c
finalproject.c — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.c).
글 읽기 →
C++
cpp
업데이트 2026-07-21
finalproject.cpp
finalproject.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.cpp).
글 읽기 →
C++
c
업데이트 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/deviceAlpha.h).
글 읽기 →
C++
c
업데이트 2026-07-21
lab8.c
lab8.c — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.c).
글 읽기 →
C++
cpp
업데이트 2026-07-21
lab8.cpp
lab8.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.cpp).
글 읽기 →