[2026] C++ Exception Performance: Zero-Cost, noexcept, and Error Codes
이 글의 핵심
C++ exception model: zero-cost on success path, cost of throw and unwind, noexcept and vector moves, frequent errors vs exceptions, and -fno-exceptions.
Introduction
C++ uses a zero-cost exception model on the success path: when no exception is thrown, cost is usually tiny. When an exception is thrown, stack unwinding is expensive. This article surveys behavior and tuning.
1. Zero-cost exception model
Success path vs throw path
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
#include <chrono>
void normalPath() {
try {
int x = 42;
int y = x * 2;
} catch (...) {
}
}
void exceptionPath() {
try {
throw std::runtime_error("error");
} catch (...) {
}
}
Typical measurements show the throw path orders of magnitude slower per iteration than straight-line code—benchmarks depend on compiler, OS, and depth.
Idea behind “zero-cost”
다음은 간단한 cpp 코드 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// Compiler / runtime model (simplified):
// 1. Success path: minimal extra code on hot path
// 2. Exception tables: metadata elsewhere
// 3. On throw: table lookup + unwind + dtors
2. Cost breakdown
What a throw does
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class Resource {
public:
Resource() { std::cout << "ctor" << std::endl; }
~Resource() { std::cout << "dtor" << std::endl; }
};
void func3() {
Resource r3;
throw std::runtime_error("error");
}
void func2() {
Resource r2;
func3();
}
void func1() {
Resource r1;
func2();
}
int main() {
try {
func1();
} catch (const std::exception& e) {
std::cout << "caught: " << e.what() << std::endl;
}
}
Costs include exception object construction, table lookup, unwinding frames, and calling destructors along the way.
Deeper stacks cost more
Throwing through many frames is slower than shallow throws—keep error handling high-level when possible.
3. Error codes vs exceptions
Shape of APIs
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <optional>
std::optional<int> divideErrorCode(int a, int b) {
if (b == 0) {
return std::nullopt;
}
return a / b;
}
int divideException(int a, int b) {
if (b == 0) {
throw std::invalid_argument("divide by zero");
}
return a / b;
}
On the success path, well-optimized code with or without try can be similar; on the error path, exceptions are usually far more expensive than returning nullopt or a sentinel.
4. noexcept optimization
noexcept and containers
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class Widget {
int* data;
public:
Widget() : data(new int(42)) {}
// Potentially-throwing move: vector may copy on growth
Widget(Widget&& other) {
data = other.data;
other.data = nullptr;
}
// noexcept move: vector can move elements
Widget(Widget&& other) noexcept {
data = other.data;
other.data = nullptr;
}
~Widget() { delete data; }
};
5. Common pitfalls
Pitfall 1: Exceptions for control flow
Prefer normal branches (if, break, return) over throw for frequent control transfer.
Pitfall 2: Frequent throws
Parsing or validation in a tight loop should usually return optional, expected, or error codes—not throw on every bad token.
Pitfall 3: Huge exception objects
Keep exception types small; avoid embedding giant buffers in the exception object.
Pitfall 4: -fno-exceptions
Embedded or extreme environments may disable exceptions entirely—use optional, expected, or error codes consistently.
6. Optimization strategies
Use noexcept where true
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class Buffer {
std::vector<int> data;
public:
Buffer(Buffer&& other) noexcept
: data(std::move(other.data)) {}
void swap(Buffer& other) noexcept {
data.swap(other.data);
}
~Buffer() noexcept = default;
};
Minimize throws
Reserve exceptions for exceptional paths (I/O open failure, invariant violation). Use error codes for expected failures (bad user input line).
Document contracts
Mark noexcept on functions that truly cannot throw; use conditional noexcept in templates.
7. Example: file processing
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <fstream>
#include <string>
#include <optional>
#include <vector>
#include <iostream>
class FileProcessor {
public:
std::vector<std::string> readLines(const std::string& filename) {
std::ifstream file(filename);
if (!file) {
throw std::runtime_error("open failed: " + filename);
}
std::vector<std::string> lines;
std::string line;
while (std::getline(file, line)) {
lines.push_back(line);
}
return lines;
}
std::optional<int> parseLine(const std::string& line) noexcept {
try {
return std::stoi(line);
} catch (...) {
return std::nullopt;
}
}
std::vector<int> processFile(const std::string& filename) {
auto lines = readLines(filename);
std::vector<int> numbers;
for (const auto& line : lines) {
if (auto num = parseLine(line)) {
numbers.push_back(*num);
}
}
return numbers;
}
};
Summary
Key points
- Zero-cost: cheap success path in typical implementations
- Throw cost: unwind + dtors—expensive
- noexcept: enables optimizations and better container behavior
- Error codes: better for frequent, expected failures
- Exceptions: better for rare, hard failures
Exceptions vs error codes
| Situation | Typical choice | Reason |
|---|---|---|
| File missing | Exception | Rare in steady state |
| Network down | Exception / typed error | Often rare |
| Parse failure per line | Error code / optional | May be frequent |
| Input validation | Error code | Frequent |
| OOM (operator new) | Exception (unless no-except build) | Rare, severe |
| Out-of-range programmer bug | Exception / assert | Not a hot path |
Practical principles
- Do not use exceptions for ordinary control flow
- Prefer error codes where failures are common
- Mark
noexceptwhen accurate - Keep destructors non-throwing
- Profile real workloads