C++은 끊임없이 진화하고 있습니다. 과거의 복잡하고 위험했던 코드들은 이제 우아하고 안전한 현대적 문법으로 대체되고 있죠. 오늘은 C++ 엔지니어라면 반드시 알아야 할, 실무의 질을 바꾸는 핵심 문법 6가지를 소개합니다.
| 기능 (Feature) | 도입 표준 | 주요 역할 |
| 구조화된 바인딩 | C++17 | 데이터 컨테이너의 직관적 해체 |
| std::views / Ranges | C++20 | 선언적 데이터 처리 및 파이프라인 |
| std::format | C++20 | 타입 안전한 고속 포맷팅 |
| std::jthread | C++20 | RAII 기반의 안전한 스레드 제어 |
| consteval | C++20 | 컴파일 타임 연산 강제 (Immediate Functions) |
| std::is_constant_evaluated | C++20 | 컴파일/런타임 문맥 판별 |
| std::expected | C++23 | 모나딕 기반의 우아한 에러 핸들링 |
1. std::views와 Ranges (C++20) — "반복문 코드 다이어트"
복잡한 필터링과 변환 로직, 이제 for문 대신 파이프라인 연산자(|)로 한 줄에 해결하세요. 원본 컨테이너를 복사하지 않는 'View' 방식이라 성능 오버헤드도 없습니다.
기본 예제
#include <vector>
#include <ranges>
#include <iostream>
int main() {
std::vector<int> nums = {1, 2, 3, 4, 5, 6};
// 짝수 추출 -> 10 곱하기
auto result = nums | std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * 10; });
for (int n : result) std::cout << n << " "; // 결과: 20 40 60
}
view를 이용한 이미지의 이진화 예제
#include <vector>
#include <ranges>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <thread>
#pragma comment(lib, "opencv_world341d.lib")
int main() {
cv::Mat mat, matGray, matGrayView, matGrayColor, matGrayViewColor;
mat = cv::imread("D:\\capture.bmp");
cv::cvtColor(mat, matGray, cv::COLOR_BGR2GRAY);
matGrayView = matGray.clone();
std::span<uchar> data_span(matGrayView.data, matGrayView.total());
// 짝수만 골라서 10을 곱한 뒤 출력하기
auto result = data_span | std::views::transform([](uchar& pixel) { return pixel = (pixel >= 50) ? 255 : 0; });
// 4. 파이프라인 구동 (평가 수행)
// Views는 지연 평가(Lazy Evaluation)되므로 루프를 돌 때 실제로 연산이 일어납니다.
for (uchar pixel : result) {
// 이 루프를 돌고 나면 원본 img.data가 직접 수정됩니다.
}
cv::cvtColor(matGray, matGrayColor, cv::COLOR_GRAY2BGR);
cv::cvtColor(matGrayView, matGrayViewColor, cv::COLOR_GRAY2BGR);
std::vector<cv::Mat> space_synchronized_images = { mat, matGrayColor, matGrayViewColor };
// 6. 나란히 가로로 붙이기
cv::Mat combined;
cv::hconcat(space_synchronized_images, combined);
cv::imshow("TEST", combined);
cv::waitKey(10000);
}
2. 구조화된 바인딩 (C++17) — "가독성 깡패"
std::pair나 struct의 멤버를 하나씩 꺼내 쓰느라 지저분했던 코드, 이제는 구조화된 바인딩으로 직관적으로 이름을 붙여 사용하세요.
#include <vector>
#include <ranges>
#include <iostream>
#include <map>
#include <string>
#include <print>
int main() {
std::map<int, std::string> users = { {10, "Test"}, {11, "My"}, };
// 옛날엔 이터레이터 돌려서 it->first, it->second로 접근했지?
for (const auto& [id, name] : users) {
// id와 name을 일반 변수처럼 바로 사용 가능!
std::println("Map {}->{}", id, name);
}
}
3. std::format (C++20) — "형식 안정성이 보장된 printf"
std::cout의 스트림 연산자 << 지옥은 이제 그만. 파이썬이나 C#처럼 간결하고 안전한 포맷팅을 지원합니다.
#include <format>
#include <iostream>
std::string s = std::format("안녕 오빠, 여기는 Visual Studio {}이야! By {}.", 2026, "제니");
std::cout << s;
이 format 기능을 이용하기 위해서는 프로젝트 속성에서 C++ 표준을 바꿔줘야 한다.

4. std::jthread (C++20) — "자원 해제 스트레스 해소"
std::thread의 고질적인 문제였던 .join() 누락 문제를 소멸자에서 자동으로 해결해 줍니다. 멀티스레드 설계 시 발생하던 크래시를 근본적으로 차단하세요.
#include <iostream>
#include <thread>
#include <chrono>
// 인자에 stop_token을 넣어두면 컴파일러가 알아서 매칭해 줌
void infinite_loop(std::stop_token token) {
while (!token.stop_requested()) { // 외부에서 멈추라고 했는지 체크
std::cout << "하드웨어 데이터 수집 중...\n";
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
std::cout << "스레드 안전하게 종료!\n";
}
int main() {
std::jthread jt(infinite_loop);
std::this_thread::sleep_for(std::chrono::seconds(2));
// 외부에서 "이제 그만!" 신호 보내기
// 호출 즉시 token.stop_requested()가 true가 됨
jt.request_stop();
}
5. consteval & is_constant_evaluated() (C++20) — "컴파일 타임의 끝판왕"
런타임 비용을 0으로 만드는 마법입니다. consteval은 함수 실행을 컴파일 타임으로 강제하여, 연산 결과를 상수처럼 즉시 박아버립니다.
consteval int square(int n) { return n * n; }
constexpr int val = square(10); // 컴파일 타임에 100으로 계산 완료!
6. std::expected (C++23) — "에러 핸들링의 혁신"
예외(Exception)와 에러 코드 사이에서 갈팡질팡하던 시대는 끝났습니다. 성공하면 '값'을, 실패하면 '에러 객체'를 반환하는 안전한 컨테이너를 사용하세요.
#include <iostream>
#include <expected>
#include <string>
// 에러 종류 정의
enum class PrintError { OutOfMaterial, FileCorrupted, HardwareFailure };
void handlePrintError(const PrintError& e) {
if (e == PrintError::OutOfMaterial) std::cerr << "[LOG] 에러: 실리콘 원료 부족!\n";
else if (e == PrintError::FileCorrupted) std::cerr << "[LOG] 에러: 파일 데이터 손상!\n";
else std::cerr << "[LOG] 에러: 하드웨어 손상!\n";
}
// 템플릿 헬퍼: 에러 처리의 공통 로직을 캡슐화
template <typename T, typename E, typename SuccessFunc>
void executeTask(std::expected<T, E>&& res, SuccessFunc&& onSuccess) {
// 마지막 체이닝 결과를 void로 캐스팅하여 '의도적으로 무시함'을 알림
static_cast<void>(
res.and_then([&](const T& val) -> std::expected<void, E> {
onSuccess(val);
return {};
})
.or_else([&](const E& err) -> std::expected<void, E> {
handlePrintError(err);
return std::unexpected(err);
})
);
}
// 3D 프린터의 상태를 나타내는 구조체
struct Sample { std::string id; int quality; };
// 샘플 제작 함수: 성공 시 Sample, 실패 시 PrintError 반환
std::expected<Sample, PrintError> createSample(int materialAmount, bool isFileValid) {
if (materialAmount < 10) return std::unexpected(PrintError::OutOfMaterial);
if (!isFileValid) return std::unexpected(PrintError::FileCorrupted);
return Sample{ "Jenny_v0.1", 100 }; // 성공!
}
int main() {
// 1. 샘플 제작 시도
executeTask(createSample(20, true), [&](const Sample& s)
{
std::cout << "성공: " << s.id << " 제작 시작!\n";
});
return 0;
}
마치며
이 문법들은 단순히 코드를 짧게 만드는 것을 넘어, 더 안전하고 예측 가능한 프로그램을 만드는 데 목적이 있습니다. 10년 전의 C++과 지금의 C++은 완전히 다른 언어라 할 만큼 발전했습니다. 오늘 당장 프로젝트에 하나씩 적용해 보세요!
'I ♥ Programming' 카테고리의 다른 글
| [C++] 반올림이 이상해!! 부동소수점 반올림의 함정: "0.4999...의 저주"와 해결책 (0) | 2026.01.29 |
|---|---|
| OpenCV로 Shape-Based Geometric Model Find 기능 사용하기 (0) | 2025.06.18 |
| XListCtrl (CXListCtrl) 클래스 (CodeProject 소스코드) (0) | 2025.06.17 |
| 문서 파일을 열고 있는 프로세스 ID 강제 종료 (0) | 2024.12.11 |
| Windows XP SP3용 그림판 EXE 파일 (mspaint.exe) (0) | 2024.06.20 |






















