Default to C++20 for portability, opt into C++23 features when your toolchain supports them. The big modern shifts:
RAII + smart pointers replace manual new/delete;
move semantics replace explicit copies; ranges + concepts (C++20) make generic code readable;
modules begin to replace header includes (still gradual adoption). Build systems converged on CMake;
vcpkg / Conan handle dependencies.
Install · buildSetup
bash
# Compilers
brew install llvm # clang++ (macOS)
sudo apt install g++ clang # Linux
# Windows: use MSVC via Visual Studio, or w64-mingw / clang via winget
# Build systems
brew install cmake ninja # the modern default
brew install meson # alternative
brew install vcpkg # Microsoft's dep manager
# Compile a single file
g++ -std=c++23 -O2 -Wall -Wextra main.cpp -o app
# CMake skeleton
mkdir build && cd build
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ..
ninja
./myapp
# Daily commands
ctest --output-on-failure # run tests via CMake
clang-format -i src/*.cpp # format
clang-tidy src/*.cpp # lint
valgrind ./app # leak check (Linux)
asan: g++ -fsanitize=address,undefined # Address + UB sanitizers
Parallel sum · ~35 linesEnd-to-end · Threaded reduce
Split a million-element vector across hardware_concurrency threads via std::async,
sum each chunk, reduce. Pure standard library — no Boost, no OpenMP.
cpp
// C++20. Build:
// g++ -std=c++20 -O2 -pthread main.cpp -o app
#include
#include
#include
#include
#include
#include
#include
// Parallel sum using futures: split, sum locally, reduce
long long parallel_sum(const std::vector& v, std::size_t threads) {
std::vector> parts;
const auto chunk = v.size() / threads;
for (std::size_t i = 0; i < threads; ++i) {
auto begin = v.begin() + i * chunk;
auto end = (i + 1 == threads) ? v.end() : begin + chunk;
parts.push_back(std::async(std::launch::async, [begin, end] {
return std::accumulate(begin, end, 0LL);
}));
}
long long total = 0;
for (auto& p : parts) total += p.get(); // wait + accumulate
return total;
}
int main() {
std::vector v(1'000'000);
std::iota(v.begin(), v.end(), 1); // 1..N
const auto threads = std::max(1u, std::thread::hardware_concurrency());
std::cout << "threads = " << threads << "\n";
std::cout << "sum = " << parallel_sum(v, threads) << "\n";
}
Best practiceGood to know
Reach for the Rule of Zero. If your class has only members that manage themselves (smart pointers, containers, strings), you write zero special members and the compiler gives you correct copy / move / destruct. Custom resource management is a code smell unless you really need it.
Pass by const T& for reads, T + move for sinks.
Avoid const T&& in signatures — it doesn’t mean what beginners think and prevents elision.
Turn on warnings + sanitizers in CI.-Wall -Wextra -Wpedantic -Werror plus -fsanitize=address,undefined catches the majority of UB before it ships. Sanitizer overhead is 2–3×, fine for tests.
Common trapsWatch out for
Iterator invalidation is silent.v.push_back(x) can reallocate; any iterator / pointer / reference to the vector is now dangling. Check container docs for which mutations invalidate which kinds of iterators.
Lambda capture by reference outlives the scope.
A capture-by-reference lambda passed to std::async or stored in a member can outlive what it borrowed. Capture by value, or use shared_ptr when the lambda must own.
Use-after-move is "valid but unspecified".
The compiler won’t stop you, but using a moved-from object beyond destruction or reassignment is undefined for some types and surprising for others. Treat moved-from as gone.
Yes. C++ remains the language of choice for performance-critical systems, game engines, embedded firmware, and financial trading. C++20 and C++23 added ranges, concepts, coroutines, and modules that make modern C++ far safer and more expressive than legacy C++11 code.
What are smart pointers in C++?
Smart pointers — unique_ptr, shared_ptr, and weak_ptr — manage heap memory automatically via RAII so you never call delete manually. Use unique_ptr for sole ownership, shared_ptr when multiple owners share a resource, and weak_ptr to break reference cycles.
What is move semantics in C++?
Move semantics let a function transfer ownership of a resource from one object to another without copying. Marked with && (rvalue reference), moves are triggered by std::move() and enable containers like std::vector to resize without copying each element.
What is RAII in C++?
RAII (Resource Acquisition Is Initialization) ties a resource's lifetime to an object's scope. The resource is acquired in the constructor and released in the destructor, guaranteeing cleanup even if an exception is thrown. Smart pointers, file streams, and lock guards all follow RAII.
What is the difference between C++17, C++20, and C++23?
C++17 added structured bindings, if constexpr, and std::optional. C++20 was the biggest overhaul in a decade, introducing modules, coroutines, concepts, ranges, and std::format. C++23 refines ranges, adds std::print, std::expected, and std::flat_map.
What are C++ modules and should I use them?
C++ modules (C++20) replace header files with compiled binary interface units, eliminating textual inclusion and dramatically cutting build times for large codebases. Declare a module with export module mylib; and import it with import mylib;. Toolchain support is mature in MSVC and Clang 16+, and improving in GCC. For new large projects targeting C++20, modules are worth adopting; for existing codebases, the migration cost is high.