콘텐츠로 이동

C++23 std::expected 오류 처리

std::expected<T, E>는 C++23에서 도입된 값 또는 오류를 담는 타입입니다. 함수가 성공하면 T 값을, 실패하면 E 오류를 반환합니다. 예외를 던지지 않으면서도 오류 정보를 명시적으로 전달할 수 있습니다.


#include <expected>
#include <string>
#include <charconv>
enum class ParseError { InvalidInput, Overflow };
std::expected<int, ParseError> parse_int(std::string_view s)
{
int result{};
auto [ptr, ec] = std::from_chars(s.begin(), s.end(), result);
if (ec == std::errc::invalid_argument)
return std::unexpected(ParseError::InvalidInput);
if (ec == std::errc::result_out_of_range)
return std::unexpected(ParseError::Overflow);
return result; // 성공
}
int main()
{
if (auto val = parse_int("42"))
std::cout << *val << '\n'; // 42
else
std::cout << "오류\n";
auto bad = parse_int("abc");
if (!bad)
// bad.error() == ParseError::InvalidInput
std::cout << "파싱 실패\n";
}

C++23은 and_then, or_else, transform, transform_error 네 가지 체이닝 연산자를 제공합니다.

#include <expected>
#include <string>
std::expected<std::string, std::string> read_file(const std::string& path);
std::expected<int, std::string> parse_config(const std::string& content);
std::expected<void, std::string> apply_config(int value);
void load_settings(const std::string& path)
{
auto result = read_file(path)
.and_then(parse_config) // 성공 시 다음 단계
.and_then(apply_config) // 성공 시 다음 단계
.transform_error([](auto& err) { // 오류 타입 변환
return "설정 로드 실패: " + err;
});
if (!result)
std::cerr << result.error() << '\n';
}

auto doubled = parse_int("21")
.transform([](int v) { return v * 2; }); // expected<int, ParseError>{42}
auto as_string = parse_int("42")
.transform([](int v) { return std::to_string(v); }); // expected<string, ParseError>

auto recovered = parse_int("bad")
.or_else([](ParseError) {
return std::expected<int, ParseError>{0}; // 기본값 반환
});
// *recovered == 0

항목std::optional<T>std::expected<T, E>
오류 정보없음 (nullopt)있음 (E 타입)
오류 종류 구분불가가능
체이닝transform, and_then동일 + or_else
용도”값이 없을 수 있는” 상황”실패 이유가 있는” 상황

6. 실전 패턴 — 파일 파싱 파이프라인

섹션 제목: “6. 실전 패턴 — 파일 파싱 파이프라인”
#include <expected>
#include <fstream>
#include <string>
enum class FileError { NotFound, PermissionDenied, ParseFailed };
std::expected<std::string, FileError> read_file(const std::string& path)
{
std::ifstream f(path);
if (!f) return std::unexpected(FileError::NotFound);
return std::string(std::istreambuf_iterator<char>(f), {});
}
std::expected<Config, FileError> load_config(const std::string& path)
{
return read_file(path)
.and_then([](const std::string& content)
-> std::expected<Config, FileError>
{
if (content.empty())
return std::unexpected(FileError::ParseFailed);
return Config::parse(content);
});
}

int val = parse_int("bad").value_or(0); // 오류 시 0 반환

std::expected는 예외 비용 없이 함수 실패 이유를 타입 시스템으로 표현합니다. and_then 체이닝으로 여러 단계를 깔끔하게 연결하고, 오류가 발생한 지점부터 자동으로 이후 단계를 건너뜁니다. 오류 이유가 중요한 시스템 경계 함수에서 std::optional 대신 사용하세요.