--- name: cpp description: "Language-specific super-code guidelines for cpp." risk: safe source: community date_added: "2026-06-16" --- # C++: Idiomatic Efficiency Reference ## Table of Contents 1. [Memory & Ownership](#memory) 2. [Modern Types & Containers](#types) 3. [Move Semantics & References](#move) 4. [Templates & Concepts](#templates) 5. [Error Handling](#errors) 6. [Concurrency](#concurrency) 7. [Anti-patterns specific to C++](#antipatterns) --- ## 1. Memory & Ownership {#memory} ```cpp // ❌ Raw new/delete Widget* w = new Widget(); // ... 15 lines later ... delete w; // ✅ auto w = std::make_unique(); ``` ```cpp // ❌ Shared ownership when unique suffices auto w = std::make_shared(); transfer(w); // only one owner // ✅ — unique_ptr; move when transferring auto w = std::make_unique(); transfer(std::move(w)); ``` ```cpp // ❌ new[] for dynamic arrays int* arr = new int[n]; // ... use ... delete[] arr; // ✅ std::vector arr(n); ``` ```cpp // ❌ Manual RAII wrapper for file/mutex FILE* f = fopen(path, "r"); // ... must remember fclose ... // ✅ std::ifstream f(path); // closes automatically at scope exit // For non-standard resources: use unique_ptr with custom deleter auto f = std::unique_ptr(fopen(path, "r"), fclose); ``` **Rule: if you type `new`, you almost certainly want `make_unique` or `make_shared`.** --- ## 2. Modern Types & Containers {#types} ```cpp // ❌ C-style string manipulation char buf[256]; sprintf(buf, "%s:%d", host, port); // ✅ auto addr = std::format("{}:{}", host, port); // C++20 // or: auto addr = host + ":" + std::to_string(port); ``` ```cpp // ❌ out-parameter for multiple returns void compute(int input, int& result, std::string& error); // ✅ struct ComputeResult { int value; std::string error; }; ComputeResult compute(int input); // or: std::pair / std::tuple with structured bindings auto [value, error] = compute(input); ``` ```cpp // ❌ Manual loop to find element int idx = -1; for (int i = 0; i < vec.size(); i++) { if (vec[i] == target) { idx = i; break; } } // ✅ auto it = std::ranges::find(vec, target); // C++20 // or: std::find(vec.begin(), vec.end(), target); ``` ```cpp // ❌ Checking .find() != .end() then accessing auto it = map.find(key); if (it != map.end()) { use(it->second); } // ✅ (C++20) if (map.contains(key)) { use(map[key]); } // or keep iterator version when you need the value without double lookup ``` **Use `std::string_view` for function parameters that don't need ownership.** --- ## 3. Move Semantics & References {#move} ```cpp // ❌ Copying a large container into a function void process(std::vector items) { ... } // copies on call // ✅ — const ref for read, move for sink void process(const std::vector& items) { ... } // read-only void consume(std::vector items) { ... } // sink: caller moves in ``` ```cpp // ❌ std::move on const object (silently copies) const std::string s = "hello"; take(std::move(s)); // still copies // ✅ — don't const things you intend to move std::string s = "hello"; take(std::move(s)); ``` ```cpp // ❌ Returning std::move from local (prevents NRVO) std::vector build() { std::vector v; // ... fill ... return std::move(v); // pessimization // ✅ — just return the local; compiler applies NRVO or implicit move return v; } ``` --- ## 4. Templates & Concepts {#templates} ```cpp // ❌ SFINAE soup template>> T square(T x) { return x * x; } // ✅ (C++20 concepts) template T square(T x) { return x * x; } ``` ```cpp // ❌ Template for one type template void log(T msg) { std::cout << msg; } // Only ever called with std::string // ✅ — don't templatize unless you need multiple types void log(std::string_view msg) { std::cout << msg; } ``` **Concepts make template errors readable — prefer them over SFINAE and static_assert.** --- ## 5. Error Handling {#errors} ```cpp // ❌ Error codes via int returns (C-style in C++) int parse(const std::string& input, Data& out); // ✅ — std::expected (C++23) or exceptions std::expected parse(const std::string& input); // or throw for exceptional conditions Data parse(const std::string& input); // throws ParseError ``` ```cpp // ❌ Catching by value (slices derived exceptions) try { ... } catch (std::exception e) { ... } // ✅ catch (const std::exception& e) { ... } ``` ```cpp // ❌ Exception in destructor ~MyClass() { if (cleanup() < 0) throw CleanupError(); // terminates // ✅ — destructors must be noexcept; log/swallow errors ~MyClass() noexcept { if (cleanup() < 0) log_error("cleanup failed"); } ``` --- ## 6. Concurrency {#concurrency} ```cpp // ❌ Manual thread + join tracking std::thread t(work); // ... must remember t.join() ... // ✅ (C++20) std::jthread t(work); // auto-joins on destruction ``` ```cpp // ❌ Lock/unlock manually mtx.lock(); data.push_back(item); mtx.unlock(); // missed on exception // ✅ { std::scoped_lock lock(mtx); data.push_back(item); } ``` ```cpp // ❌ Polling a shared bool for completion while (!done.load()) { std::this_thread::sleep_for(10ms); } // ✅ — use std::future or condition_variable auto future = std::async(std::launch::async, compute); auto result = future.get(); ``` **Use `std::scoped_lock` over `lock_guard` — it handles multiple mutexes and avoids deadlock.** --- ## 7. Anti-patterns specific to C++ {#antipatterns} | Anti-pattern | Preferred | |---|---| | Raw `new`/`delete` | `make_unique` / `make_shared` | | `(Type)expr` C-style cast | `static_cast(expr)` | | `#define` constants | `constexpr` variables | | `NULL` | `nullptr` | | `using namespace std;` in headers | explicit `std::` prefix | | Manual loop for transform/filter | `std::ranges` or `` | | `std::endl` | `'\n'` (endl flushes — slow) | | `char*` for string parameters | `std::string_view` | | Exception specification `throw()` | `noexcept` | | Inheriting from `std::` containers | composition, not inheritance | | `volatile` for thread synchronization | `std::atomic` | | Header-only mega-templates | separate declaration/definition where compile time matters | ## Limitations - These are language-specific guidelines and do not cover overall architectural decisions. - Over-compression might reduce readability; apply judgement.