- C++ 표준 라이브러리는 C++11 이후 잘못된 설계를 정식 폐기하거나 새 대체 기능 옆에 방치하는 일을 반복했고, 개발자가 “쓰지 말아야 할 계층”의 시기를 알아야 하는 구조임
- 공식 철회 계층에는 std::auto_ptr, 동적 예외 명세, C++11 가비지 컬렉션 인터페이스, std::aligned_storage처럼 폐기·제거 논문이 붙은 항목들이 있으며, std::function도 std::move_only_function, std::copyable_function, std::function_ref로 둘러싸인 15년짜리 대체 흐름에 놓임
- 비공식 회피 계층에는 느린 std::regex, 소멸자에서 대기해 교착 함정을 만드는 std::async, <iostream>, std::list, std::deque, std::vector<bool>처럼 표준에는 남아 있지만 프로덕션 코드가 우회하는 기능들이 자리함
- 기본 컨테이너 문제는 std::unordered_map, std::map, std::list에서 두드러지며, 동일 워크로드 벤치마크에서 C++ 순진 구현의 P99가 302,653 cycles, Rust 순진 구현이 5,177 cycles로 58배 차이를 보임
- ABI 안정성 선택은 다른 언어가 삭제, 에디션, 주요 버전 전환으로 실수를 줄이는 방식과 달리 C++의 잘못된 기본값을 std:: 안에 사실상 영구 보존하는 핵심 차이임
출발점: std::function의 “레거시” 판정
- Sandor Dargo의 std::copyable_function 빠른 참조 표는 std::function을 “Legacy. Avoid in new code.”로 분류함
- std::function은 C++11에 들어갔고 최신 대체 래퍼인 std::copyable_function은 C++26에 들어가며, 새 기능의 권장점은 “복사 가능한 호출 가능 객체가 필요할 때 쓰라”가 아니라 “원래 것을 쓰지 말라”는 쪽에 가까움
- std::function의 const operator()는 non-const 호출 가능 객체를 호출하는 const-correctness 결함이 있고, ABI를 깨지 않고는 고칠 수 없는 상태임
- 위 결함의 대응으로 std::move_only_function은 C++23의 P0288R9, std::copyable_function은 C++26의 P2548R6, std::function_ref는 C++26의 P0792R14 흐름에 놓임
공식으로 되돌린 표준 기능들
- std::auto_ptr는 복사-이동 의미가 제네릭 코드와 표준 컨테이너를 깨뜨려 C++11에서 폐기 예정, C++17에서 N4190로 제거됐고, 같은 논문은 C++98 <functional> 어댑터들과 std::random_shuffle도 제거함
- std::random_shuffle은 std::rand와 전역 상태에 의존했기 때문에 std::shuffle로 대체됨
- 동적 예외 명세 throw(X, Y)는 C++11에서 폐기 예정, C++17에서 P0003R5로 제거됐고, throw() 별칭은 C++20의 P1152로 제거됨
- std::iterator는 C++17에서 P0174R2로 폐기 예정이 됐고 C++26 제거가 P3365R1에서 추진되며, 대체 방식은 다섯 typedef를 직접 정의하는 것임
- std::aligned_storage와 std::aligned_union은 C++11에 들어갔다가 C++23의 P1413R3로 폐기 예정이 됐고, typename ::type 보일러플레이트, reinterpret_cast, Len == 0의 정의되지 않은 동작, constexpr 부재가 문제로 꼽힘
- std::not1, std::not2, unary_negate, binary_negate는 C++17에서 폐기 예정, C++20에서 제거됐고 P0005의 std::not_fn으로 대체됨
- C++11 가비지 컬렉션 인터페이스인 std::declare_reachable 계열은 주요 구현체가 실제 가비지 컬렉터를 제공하지 않은 채 C++23의 P2186R2로 제거됨
- Concepts TS, Modules TS, Coroutines TS, Reflection TS, Executors TS, Networking TS도 병합 전 재설계·교체·지연을 거쳤고, Reflection은 P2996, Executors는 P2300 sender/receiver 흐름으로 바뀜
표준에는 남았지만 현장에서 피하는 기능들
- std::regex는 C++11에 들어갔지만 P1844R1이 “다른 사용 가능한 해법에 비해 성능이 매우 나쁘다”는 위원회 기록을 남겼고, 대체 흐름은 CTRE와 P1433R0, 표준 밖에서는 Boost.Regex, RE2, PCRE2 쪽임
- std::async는 반환된 future의 소멸자가 비동기 작업 완료까지 블록하며, N3679는 이로 인한 교착 함정을 기록함
- <iostream>은 느리고 locale에 묶이며 포맷팅에서 스레드 안전하지 않고 오류 메시지가 악명 높지만, C++20의 P0645 std::format과 C++23의 P2093 std::print·std::println이 들어온 뒤에도 폐기 예정이 아님
- std::list는 Bjarne Stroustrup이 2012 GoingNative keynote에서 중간 삽입 워크로드조차 std::vector가 이긴다고 보인 항목이며, 후속 글 Are lists evil?의 답도 “그렇다”에 가까움
- std::deque는 Microsoft STL의 공개 이슈 microsoft/STL#147에서 표준이 강제한 블록 크기가 너무 작고 다음 ABI break 때 대대적 성능 개편이 필요하다고 기록된 항목임
- std::valarray는 1998년 수치 컨테이너로 들어갔지만 표현식 템플릿 최적화가 실현되지 않았고, cppreference 기준으로 구현체들이 일반 컨테이너 이상의 특별 코드를 갖지 않는 것으로 보임
- std::vector<bool>는 Howard Hinnant의 On vector<bool>이 대표 분석이며, bit-packed 저장 자체는 유용하지만 std::vector 특수화처럼 이름 붙어 제네릭 코드에서 T = bool일 때 잘못된 동작을 유발하는 함정임
- volatile은 C++20의 P1152R4로 복합 연산과 매개변수·반환 위치에서 폐기 예정이 됐다가 C++23의 P2327R1로 일부 되돌아갔고, C++26의 P2866R0에서 추가 되돌림이 예정됨
ABI로 고칠 수 없는 기본 컨테이너
- std::unordered_map은 C++11 사양의 버킷과 반복자 안정성 때문에 open addressing을 사실상 금지하며, Google SwissTable 구조는 std::unordered_map 대비 약 3배 성능 우위를 보인 것으로 제시됨
- Folly F14, Boost unordered_flat_map, ankerl::unordered_dense 등도 유사한 방향의 대체품이며, Rust HashMap은 hashbrown SwissTable 포트를 표준 라이브러리 기본값으로 씀
- std::map과 std::set은 노드 기반 red-black tree라 노드마다 힙 할당이 필요하고 순회마다 포인터 추적을 하며, Abseil btree_map과 Rust BTreeMap은 B-tree 기반으로 같은 문제를 피함
- C++23은 P0429R9로 std::flat_map과 std::flat_set을 추가했지만, std::unordered_map, std::map, std::list 자체의 기본 설계는 바꾸지 못함
- multi-symbol order book benchmark는 같은 워크로드, 같은 seed, 같은 격리 코어에서 C++의 std::unordered_map+std::map+std::list와 Rust의 HashMap+BTreeMap+VecDeque를 비교함
| C++ naive (unordered_map + map + list) | 302,653 |
| C++ step 1 (flat_hash_map + map + deque) | 9,951 |
| C++ step 2 (flat_hash_map + btree_map + deque) | 9,114 |
| C++ step 3 (flat_hash_map + btree_map + vector) | 4,268 |
| Rust naive (HashMap + BTreeMap + VecDeque) | 5,177 |
- std::list에서 std::vector 전환만 약 70배, std::unordered_map에서 flat_hash_map 전환은 3~5배, std::map에서 btree_map 전환은 1.09배이자 잡음 범위 안의 효과를 보임
- 비교의 초점은 Rust 언어 자체가 C++보다 58배 빠르다는 뜻이 아니라, Rust 표준 라이브러리가 올바른 기본값을 택했고 C++ 표준 라이브러리는 ABI 때문에 세 기본값을 고치지 못한다는 점임
Vasa 문제와 기능 축적
- Bjarne Stroustrup의 2018년 WG21 문서 P0977R0 “Remember the Vasa!”는 1628년 스웨덴 전함 Vasa의 침몰을 비유로 들며, 위원회에 “약 150명의 요리사”가 있고 개별 기능이 전체 시스템에 미치는 영향을 충분히 다루지 않는다고 진단함
- std::simd는 std::simd Is a Solution to the Wrong Problem에서 같은 패턴의 대표 항목으로 다뤄지며, Matthias Kretz가 Vc 라이브러리에서 시작해 P0214, Parallelism TS 2, P1928을 거쳐 C++26에 넣은 기능임
- std::simd가 표준에 들어올 때 표준 밖에는 Google Highway, ISPC, EVE, xsimd, SIMDe가 있었고, GCC와 Clang의 auto-vectorizer도 개선돼 -O3 -march=native 스칼라 루프가 std::simd보다 낫다는 결과가 나온 것으로 제시됨
- std::simd는 동등한 스칼라 코드보다 컴파일이 10배 느리고, 대체하려던 auto-vectorizer보다 느리며, ARM SVE의 scalable-width 벡터와 runtime dispatch를 표현하지 못한다는 문제를 가짐
- libstdc++, libc++, MSVC STL 세 구현체는 각각 한 자릿수 규모의 엔지니어 팀이 유지하며, 새 표준 기능은 테스트 매트릭스, 적합성 버그, 기능 간 상호작용, 다음 유지보수자가 물려받을 버그 트래커 항목을 늘림
- std::regex는 15년 동안 알려진 문제를 안고 있고, std::deque는 재설계 필요 이슈를 갖고 있으며, C++20 modules는 표준화 6년 뒤에도 세 구현체 전체에서 깔끔하게 동작하지 않는 상태로 묘사됨
- 현대 C++ 표준의 실제 운용 지식은 잘못된 계층의 시기, 서드파티 우회책, 세 표준 라이브러리 구현 차이, 이론과 실무의 차이를 익힌 소수의 전업 전문가에게 집중되는 구조임
다른 언어와의 차이: 실수 여부가 아니라 보존율
- Python은 PEP 594로 20개 이상의 표준 라이브러리 모듈을 제거했고, PEP 632로 distutils를 Python 3.12에서 제거했으며, PEP 387은 위험하거나 깨진 기능의 폐기 주기 단축 권한을 명시함
- Java는 Applet API를 Java 9에서 폐기 예정, Java 17에서 제거 예정, JEP 504에서 실제 제거까지 8년 경로로 처리했고, Nashorn은 JEP 372로 Java 15에서 제거됨
- Java SecurityManager는 JEP 411로 제거 예정 폐기 상태가 됐고 JEP 486으로 영구 비활성화됐으며, JEP 398은 Applet API 제거 예정 경로를 다룸
- Rust는 2015, 2018, 2021, 2024 에디션을 Cargo.toml에서 crate별 opt-in으로 선택하며, mem::uninitialized는 MaybeUninit, std::error::Error::description은 source, try! 매크로는 ? 연산자로 대체됨
- C#은 .NET Framework에서 .NET Core로 넘어가며 BinaryFormatter, AppDomains, Remoting, Code Access Security, WCF server, WebForms를 떨어뜨린 주요 버전 전환을 감수함
- JavaScript는 웹 호환성 제약 때문에 거의 제거하지 않지만, cancelable promises는 Stage 1에서 철회됐고 SIMD.js는 WebAssembly SIMD 쪽으로 버려졌으며, Go는 Go 1 호환성 약속 때문에 io/ioutil을 폐기 예정으로만 남기는 쪽을 택함
- C++의 차이는 실수를 했는지가 아니라 std::regex, std::unordered_map, std::vector<bool>, std::valarray, std::function const-correctness 결함 같은 항목을 거의 제거하지 못하는 보존율임
ABI 안정성이 만드는 영구 보존
- P1863R1 “ABI - Now or Never”는 C++23에서 ABI break를 감수할지 영구 ABI 안정성을 택할지 묻는 흐름이었고, 위원회는 사실상 영구 ABI 안정성을 택함
- 이 선택 때문에 std::regex 수정, std::unordered_map의 open-addressing 전환, std::list, std::map, std::deque의 구조 변경이 어려워짐
- C++ 표준 라이브러리 ABI는 동적 링커가 강제하며, 한 libstdc++ 릴리스로 컴파일된 객체가 다른 릴리스의 객체와 연결돼야 하므로 std::string 레이아웃과 std::regex_traits 구성 같은 세부가 배포 바이너리에 고정됨
- 이 제약은 libstdc++ ABI policy와 Itanium C++ ABI 같은 문서에서 구체화됨
- Python 사용자는 python==3.12, Rust 사용자는 Cargo.toml 에디션, Java 사용자는 JDK 버전, C# 사용자는 net6.0이나 net8.0 TFM을 고르지만, C++에는 std::용 Cargo.toml이 없음
- -std=c++26은 어떤 헤더와 언어 규칙을 쓸지 고르지만, 다른 std::string이나 재설계된 std::unordered_map을 제공하지 않음
- 그래서 2026년에 프로덕션으로 내보내는 C++ 표준 라이브러리는 1998년 이후 위원회가 받아들인 잘못된 기본값들을 설계와 강제력에 의해 계속 품는 상태임
- tier-one trading firm, 검색 엔진, 브라우저의 현대 C++ 코드베이스는 Boost, Abseil, Folly, EASTL, Chromium //base, 손수 만든 컨테이너, 커스텀 allocator, CTRE, Outcome, coroutine 라이브러리 같은 비표준 라이브러리에 크게 기대는 구조임

5 days ago
4








English (US) ·