{ "cells": [ { "cell_type": "markdown", "id": "3c1e938d-bbc3-4191-b9c2-518d903f18a3", "metadata": {}, "source": [ "# C++ Advanced" ] }, { "cell_type": "markdown", "id": "391791ef-e2c8-4725-8e1a-9b162aaa9b3a", "metadata": {}, "source": [ "## [C++ Template Metaprogramming](https://www.youtube.com/playlist?list=PLWxziGKTUvQFIsbbFcTZz7jOT4TMGnZBh)\n", "\n", "* Youtube Playlist by BitsOfQ" ] }, { "cell_type": "markdown", "id": "5b7a58a1-6e47-4aa4-aa2e-ba6e945bbcf5", "metadata": {}, "source": [ "## Class Template Argument Deduction (CTAD)\n", "\n", "* Starting C++17, compiler can infer types\n", "```c++\n", "std::pair p(42, 3.14); // without CTAD\n", "std::pair p(42, 3.14); // with CTAD, only legal in C++17 and beyond\n", "```\n", "* 在 `std::pair` 這樣的 simple case 很方便,但情況複雜時程式會變難讀,特別是有 template specialization 時應該避免使用" ] }, { "cell_type": "markdown", "id": "1e679534-8a82-4ae6-ba8f-ea8dca4f471e", "metadata": {}, "source": [ "## CTAD and Template Partial Specialization\n", "\n", "* 下面這段 code 編譯不過,因為\n", " 1. Compiler 決定 `map` 是 `Map`\n", " 2. Compiler 抓 partial specialization `Map` 來用\n", " 3. 但 `Map` 沒有對應的 constructor 於是報錯\n", "\n", "```c++\n", "#include \n", "\n", "template\n", "struct Map {\n", " Map(KEY a, DATA b) {\n", " std::cout << \"Map\" << std::endl;\n", " }\n", "};\n", "\n", "template\n", "struct Map {\n", " Map() {\n", " std::cout << \"Map\" << std::endl;\n", " }\n", "};\n", "\n", "int main() {\n", " auto map = Map{1, true};\n", "}\n", "```" ] }, { "cell_type": "markdown", "id": "6b492a78-0145-45c0-9deb-134a481223ba", "metadata": {}, "source": [ "## `std::stringstream`\n", "\n", "* 用 `std::stringstream` 要先 `#include `\n", "* 下面兩種寫法都不行。Operator `<<` 是用來把 data 插入已存在的 stream\n", "```c++\n", "std::stringstream ss = \"a\" << \"b\" << \"c\";\n", "\n", "```\n", "```c++\n", "std::stringstream ss;\n", "ss = \"a\" << \"b\" << \"c\";\n", "\n", "```\n", "* 要這樣寫才對\n", "```c++\n", "std::stringstream ss;\n", "ss << \"a\" << \"b\" << \"c\";\n", "ss.str()\n", "```" ] }, { "cell_type": "markdown", "id": "a41f80a6-4e91-4d3b-ad9f-499e4bab6ade", "metadata": {}, "source": [ "## Always Pass `int` By Value\n", "\n", "* 雖然 `void foo(const int& a){ ... }` 不用複製,reference mechanism 也有 overhead,結果可能還更慢\n", "* Same goes for `double`, `bool`, `chat` 和 enums\n", "* 如果一個類型不超過 2 bytes,並且複製它的成本很低,應該 pass by value" ] }, { "cell_type": "markdown", "id": "76771da8-9545-4f69-96a2-f9a833e9c2be", "metadata": {}, "source": [ "## STL Containers\n", "\n", "\n", "| 容器名稱 | 標頭檔 | 底層實作結構 | \n", "|--------------------------------------|----------------------|-------------------------------| \n", "| array | array | 固定大小陣列 | \n", "| vector | vector | 動態陣列(連續記憶體) | \n", "| deque | deque | 雙端陣列(分段連續記憶體) | \n", "| forward_list | forward_list | linked list | \n", "| list | list | doubly linked list | \n", "| set, multiset | set | RB tree | \n", "| map, multimap | map | RB tree | \n", "| unordered_set, unordered_multiset | unordered_set | hash table | \n", "| unordered_map, unordered_multimap | unordered_map | hash table | \n", "| stack | stack | deque by default | \n", "| queue | queue | deque by default | \n", "| priority_queue | queue | heap with vector | \n", "\n", "\n", "\n", "| 容器名稱 | 插入/新增 | 刪除 | 查找 | \n", "|--------------------------------------|---------------------|-------------------|-------------------| \n", "| array | O(1) | O(1) | O(1) | \n", "| vector | 平均 O(1) | O(n) | O(n) | \n", "| deque | O(1) | O(1) | O(n) | \n", "| forward_list | O(1) | O(1) | O(n) | \n", "| list | O(1) | O(1) | O(n) | \n", "| set, multiset | O(log n) | O(log n) | O(log n) | \n", "| map, multimap | O(log n) | O(log n) | O(log n) | \n", "| unordered_set, unordered_multiset | 平均 O(1) | 平均 O(1) | 平均 O(1) | \n", "| unordered_map, unordered_multimap | 平均 O(1) | 平均 O(1) | 平均 O(1) | \n", "| stack | O(1)(push/pop) | O(1) | 不支援查找 | \n", "| queue | O(1)(push/pop) | O(1) | 不支援查找 | \n", "| priority_queue | O(log n)(push/pop) | O(log n) | O(n) | " ] }, { "cell_type": "markdown", "id": "42f3bbf3-acfc-4d40-8213-bc8ef0e91af0", "metadata": {}, "source": [ "## [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) (有錯,例如函式命名規則)\n", "\n", "### 核心原則\n", "\n", "* 為讀者優化:程式碼是寫一次、讀很多次,風格應以「易讀、易維護」為優先\n", "* 一致性至上:整體風格一致,便於協作與工具自動化處理\n", "* 避免危險語法:限制使用容易出錯或難以維護的 C++ 特性\n", "* 考量規模與可擴展性:大型程式碼庫需避免污染全域命名空間、減少依賴\n", "* 合理使用現代 C++ 特性:鼓勵使用 C++20,但避免使用尚未普及的 C++23 特性\n", "\n", "### 命名規則\n", "\n", "| 類型 | 命名風格 | 範例 |\n", "|--------------|----------------|--------------------|\n", "| 變數 | `snake_case` | `total_count` |\n", "| 類別 / 結構 | `PascalCase` | `DataProcessor` |\n", "| 常數 | `kPascalCase` | `kMaxSize` |\n", "| 函式 | `snake_case()` | `process_data()` |\n", "| 巨集 | `ALL_CAPS` | `MAX_BUFFER_SIZE` |\n", "\n", "### 檔案與結構\n", "\n", "* 每個 `.cc` 檔應有對應的 `.h` 檔\n", "* Header 檔需使用 `#define` guard 或 `#pragma once`\n", "* Include 順序:相關 header → C 標準庫 → C++ 標準庫 → 第三方 → 專案內部\n", "\n", "### 類別設計\n", "\n", "* 使用 `class` 表示有行為的物件,`struct` 僅用於純資料容器\n", "* 成員變數使用 `private`,命名加底線:`foo_`\n", "* 建構子只做初始化,複雜邏輯應放在 `Init()`\n", "* 禁用不必要的複製與指派操作:使用 `= delete`\n", "\n", "### 函式設計\n", "\n", "* 函式應短小(建議 <40 行),聚焦單一責任\n", "* 輸入參數優先,輸出參數其後\n", "* 優先使用回傳值而非輸出參數\n", "* 禁用預設參數(除非特殊情況)\n", "\n", "### 其他語法建議\n", "\n", "* 禁用 `using namespace`\n", "* 禁用 C++ 例外(`throw` / `try` / `catch`)\n", "* 禁用 RTTI(`typeid` / `dynamic_cast`)\n", "* 使用 `std::unique_ptr` 管理資源,避免裸指標\n", "* 使用 `const` 修飾不可變參數與變數" ] }, { "cell_type": "markdown", "id": "bad6fd62-7285-4108-9fde-d58e601dcff1", "metadata": {}, "source": [ "## Keeping `std::function` Alive in Lambda" ] }, { "cell_type": "code", "execution_count": null, "id": "8f39ad51-3ccd-4873-bb36-05c479070792", "metadata": {}, "outputs": [], "source": [ "// crash\n", "\n", "#include \n", "\n", "class Function {\n", "public: \n", " Function(std::function func) : m_func(func) {}\n", "\n", " double operator()(double t) const {\n", " return m_func(t);\n", " }\n", " Function operator+(const Function& other) const {\n", " return Function([=](double t){ return m_func(t) + other.m_func(t); });\n", " }\n", "private: \n", " std::function m_func;\n", "};\n", "\n", "Function f1([](double t) { return 1.; }), \n", " f2([](double t) { return 10.; }), \n", " f3([](double t) { return 100.; });\n", "\n", "Function h = f1 + f2 + f3;\n", "\n", "// h(3.5) // crash: temp obj (f1 + f2) is gone" ] }, { "cell_type": "code", "execution_count": 1, "id": "baa3c6e4-8b2a-4a2f-9b1d-add988a208e8", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "111.00000" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// working\n", "\n", "#include \n", "#include \n", "\n", "class Function {\n", "public: \n", " Function(std::function func) : m_func(func) {}\n", "\n", " double operator()(double t) const {\n", " return m_func(t);\n", " }\n", " Function operator+(const Function& other) const {\n", " auto shared_this = std::make_shared(*this);\n", " auto shared_other = std::make_shared(other);\n", " return Function([=](double t){ return (shared_this->m_func)(t) + (shared_other->m_func)(t); });\n", " }\n", "private: \n", " std::function m_func;\n", "};\n", "\n", "Function f1([](double t) { return 1.; }), \n", " f2([](double t) { return 10.; }), \n", " f3([](double t) { return 100.; });\n", "\n", "Function h = f1 + f2 + f3;\n", "\n", "h(3.5)" ] } ], "metadata": { "kernelspec": { "display_name": "C++17", "language": "C++17", "name": "xcpp17" }, "language_info": { "codemirror_mode": "text/x-c++src", "file_extension": ".cpp", "mimetype": "text/x-c++src", "name": "c++", "version": "17" } }, "nbformat": 4, "nbformat_minor": 5 }