C++ Advanced
C++ Template Metaprogramming
Youtube Playlist by BitsOfQ
Class Template Argument Deduction (CTAD)
Starting C++17, compiler can infer types
std::pair<int, double> p(42, 3.14); // without CTAD
std::pair p(42, 3.14); // with CTAD, only legal in C++17 and beyond
在
std::pair這樣的 simple case 很方便,但情況複雜時程式會變難讀,特別是有 template specialization 時應該避免使用
CTAD and Template Partial Specialization
下面這段 code 編譯不過,因為
Compiler 決定
map是Map<int, bool>Compiler 抓 partial specialization
Map<int, DATA>來用但
Map<int, DATA>沒有對應的 constructor 於是報錯
#include <iostream>
template<typename KEY, typename DATA>
struct Map {
Map(KEY a, DATA b) {
std::cout << "Map" << std::endl;
}
};
template<typename DATA>
struct Map<int, DATA> {
Map() {
std::cout << "Map<int, DATA>" << std::endl;
}
};
int main() {
auto map = Map{1, true};
}
std::stringstream
用
std::stringstream要先#include <sstream>下面兩種寫法都不行。Operator
<<是用來把 data 插入已存在的 stream
std::stringstream ss = "a" << "b" << "c";
std::stringstream ss;
ss = "a" << "b" << "c";
要這樣寫才對
std::stringstream ss;
ss << "a" << "b" << "c";
ss.str()
Always Pass int By Value
雖然
void foo(const int& a){ ... }不用複製,reference mechanism 也有 overhead,結果可能還更慢Same goes for
double,bool,chat和 enums如果一個類型不超過 2 bytes,並且複製它的成本很低,應該 pass by value
STL Containers
容器名稱 |
標頭檔 |
底層實作結構 |
|---|---|---|
array |
array |
固定大小陣列 |
vector |
vector |
動態陣列(連續記憶體) |
deque |
deque |
雙端陣列(分段連續記憶體) |
forward_list |
forward_list |
linked list |
list |
list |
doubly linked list |
set, multiset |
set |
RB tree |
map, multimap |
map |
RB tree |
unordered_set, unordered_multiset |
unordered_set |
hash table |
unordered_map, unordered_multimap |
unordered_map |
hash table |
stack |
stack |
deque by default |
queue |
queue |
deque by default |
priority_queue |
queue |
heap with vector |
容器名稱 |
插入/新增 |
刪除 |
查找 |
|---|---|---|---|
array |
O(1) |
O(1) |
O(1) |
vector |
平均 O(1) |
O(n) |
O(n) |
deque |
O(1) |
O(1) |
O(n) |
forward_list |
O(1) |
O(1) |
O(n) |
list |
O(1) |
O(1) |
O(n) |
set, multiset |
O(log n) |
O(log n) |
O(log n) |
map, multimap |
O(log n) |
O(log n) |
O(log n) |
unordered_set, unordered_multiset |
平均 O(1) |
平均 O(1) |
平均 O(1) |
unordered_map, unordered_multimap |
平均 O(1) |
平均 O(1) |
平均 O(1) |
stack |
O(1)(push/pop) |
O(1) |
不支援查找 |
queue |
O(1)(push/pop) |
O(1) |
不支援查找 |
priority_queue |
O(log n)(push/pop) |
O(log n) |
O(n) |
Google C++ Style Guide (有錯,例如函式命名規則)
核心原則
為讀者優化:程式碼是寫一次、讀很多次,風格應以「易讀、易維護」為優先
一致性至上:整體風格一致,便於協作與工具自動化處理
避免危險語法:限制使用容易出錯或難以維護的 C++ 特性
考量規模與可擴展性:大型程式碼庫需避免污染全域命名空間、減少依賴
合理使用現代 C++ 特性:鼓勵使用 C++20,但避免使用尚未普及的 C++23 特性
命名規則
類型 |
命名風格 |
範例 |
|---|---|---|
變數 |
|
|
類別 / 結構 |
|
|
常數 |
|
|
函式 |
|
|
巨集 |
|
|
檔案與結構
每個
.cc檔應有對應的.h檔Header 檔需使用
#defineguard 或#pragma onceInclude 順序:相關 header → C 標準庫 → C++ 標準庫 → 第三方 → 專案內部
類別設計
使用
class表示有行為的物件,struct僅用於純資料容器成員變數使用
private,命名加底線:foo_建構子只做初始化,複雜邏輯應放在
Init()禁用不必要的複製與指派操作:使用
= delete
函式設計
函式應短小(建議 <40 行),聚焦單一責任
輸入參數優先,輸出參數其後
優先使用回傳值而非輸出參數
禁用預設參數(除非特殊情況)
其他語法建議
禁用
using namespace禁用 C++ 例外(
throw/try/catch)禁用 RTTI(
typeid/dynamic_cast)使用
std::unique_ptr管理資源,避免裸指標使用
const修飾不可變參數與變數
Keeping std::function Alive in Lambda
[ ]:
// crash
#include <functional>
class Function {
public:
Function(std::function<double(double)> func) : m_func(func) {}
double operator()(double t) const {
return m_func(t);
}
Function operator+(const Function& other) const {
return Function([=](double t){ return m_func(t) + other.m_func(t); });
}
private:
std::function<double(double)> m_func;
};
Function f1([](double t) { return 1.; }),
f2([](double t) { return 10.; }),
f3([](double t) { return 100.; });
Function h = f1 + f2 + f3;
// h(3.5) // crash: temp obj (f1 + f2) is gone
[1]:
// working
#include <functional>
#include <memory>
class Function {
public:
Function(std::function<double(double)> func) : m_func(func) {}
double operator()(double t) const {
return m_func(t);
}
Function operator+(const Function& other) const {
auto shared_this = std::make_shared<Function>(*this);
auto shared_other = std::make_shared<Function>(other);
return Function([=](double t){ return (shared_this->m_func)(t) + (shared_other->m_func)(t); });
}
private:
std::function<double(double)> m_func;
};
Function f1([](double t) { return 1.; }),
f2([](double t) { return 10.; }),
f3([](double t) { return 100.; });
Function h = f1 + f2 + f3;
h(3.5)
[1]:
111.00000