# C++ 표준 라이브러리는 15년 동안 스스로 철회해 왔고, 그 증거는 공개돼 있음

> Clean Markdown view of GeekNews topic #30187. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=30187](https://news.hada.io/topic?id=30187)
- GeekNews Markdown: [https://news.hada.io/topic/30187.md](https://news.hada.io/topic/30187.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-06-05T09:04:44+09:00
- Updated: 2026-06-05T09:04:44+09:00
- Original source: [hftuniversity.com](https://hftuniversity.com/post/the-c-standard-library-has-been-walking-itself-back-for-fifteen-years-and-the-receipts-are-public)
- Points: 1
- Comments: 2

## Topic Body

- **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`, `&lt;iostream&gt;`, `std::list`, `std::deque`, `std::vector&lt;bool&gt;`처럼 표준에는 남아 있지만 프로덕션 코드가 우회하는 기능들이 자리함
- **기본 컨테이너** 문제는 `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`](https://www.sandordargo.com/blog/2026/05/20/cpp26-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](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0288r9.html), `std::copyable_function`은 C++26의 [P2548R6](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2548r6.pdf), `std::function_ref`는 C++26의 [P0792R14](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p0792r14.html) 흐름에 놓임

### 공식으로 되돌린 표준 기능들
- `std::auto_ptr`는 복사-이동 의미가 제네릭 코드와 표준 컨테이너를 깨뜨려 C++11에서 폐기 예정, C++17에서 [N4190](https://isocpp.org/files/papers/N4190.txt)로 제거됐고, 같은 논문은 C++98 `&lt;functional&gt;` 어댑터들과 `std::random_shuffle`도 제거함
- `std::random_shuffle`은 `std::rand`와 전역 상태에 의존했기 때문에 `std::shuffle`로 대체됨
- 동적 예외 명세 `throw(X, Y)`는 C++11에서 폐기 예정, C++17에서 P0003R5로 제거됐고, `throw()` 별칭은 C++20의 [P1152](https://wg21.link/p1152r4)로 제거됨
- `std::iterator`는 C++17에서 [P0174R2](https://www.open-std.org/JTC1/SC22/wg21/docs/papers/2016/p0174r1.html)로 폐기 예정이 됐고 C++26 제거가 [P3365R1](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3365r1.pdf)에서 추진되며, 대체 방식은 다섯 typedef를 직접 정의하는 것임
- `std::aligned_storage`와 `std::aligned_union`은 C++11에 들어갔다가 C++23의 [P1413R3](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1413r3.pdf)로 폐기 예정이 됐고, `typename ::type` 보일러플레이트, `reinterpret_cast`, `Len == 0`의 정의되지 않은 동작, constexpr 부재가 문제로 꼽힘
- `std::not1`, `std::not2`, `unary_negate`, `binary_negate`는 C++17에서 폐기 예정, C++20에서 제거됐고 [P0005](https://wg21.link/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](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2996r4.html), Executors는 [P2300](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2300r10.html) sender/receiver 흐름으로 바뀜

### 표준에는 남았지만 현장에서 피하는 기능들
- `std::regex`는 C++11에 들어갔지만 [P1844R1](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1844r1.html)이 “다른 사용 가능한 해법에 비해 성능이 매우 나쁘다”는 위원회 기록을 남겼고, 대체 흐름은 CTRE와 [P1433R0](https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2019/p1433r0.pdf), 표준 밖에서는 Boost.Regex, RE2, PCRE2 쪽임
- `std::async`는 반환된 future의 소멸자가 비동기 작업 완료까지 블록하며, [N3679](https://open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3679.html)는 이로 인한 교착 함정을 기록함
- `&lt;iostream&gt;`은 느리고 locale에 묶이며 포맷팅에서 스레드 안전하지 않고 오류 메시지가 악명 높지만, C++20의 [P0645](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r10.html) `std::format`과 C++23의 [P2093](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2093r14.html) `std::print`·`std::println`이 들어온 뒤에도 폐기 예정이 아님
- `std::list`는 Bjarne Stroustrup이 [2012 GoingNative keynote](https://learn.microsoft.com/en-us/shows/goingnative-2012/keynote-bjarne-stroustrup-cpp11-style)에서 중간 삽입 워크로드조차 `std::vector`가 이긴다고 보인 항목이며, 후속 글 [Are lists evil?](https://isocpp.org/blog/2014/06/stroustrup-lists)의 답도 “그렇다”에 가까움
- `std::deque`는 Microsoft STL의 공개 이슈 [microsoft/STL#147](https://github.com/microsoft/STL/issues/147)에서 표준이 강제한 블록 크기가 너무 작고 다음 ABI break 때 대대적 성능 개편이 필요하다고 기록된 항목임
- `std::valarray`는 1998년 수치 컨테이너로 들어갔지만 표현식 템플릿 최적화가 실현되지 않았고, cppreference 기준으로 구현체들이 일반 컨테이너 이상의 특별 코드를 갖지 않는 것으로 보임
- `std::vector&lt;bool&gt;`는 Howard Hinnant의 [On `vector&lt;bool&gt;`](https://howardhinnant.github.io/onvectorbool.html)이 대표 분석이며, bit-packed 저장 자체는 유용하지만 `std::vector` 특수화처럼 이름 붙어 제네릭 코드에서 `T = bool`일 때 잘못된 동작을 유발하는 함정임
- `volatile`은 C++20의 [P1152R4](https://wg21.link/p1152r4)로 복합 연산과 매개변수·반환 위치에서 폐기 예정이 됐다가 C++23의 [P2327R1](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2327r1.pdf)로 일부 되돌아갔고, C++26의 [P2866R0](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2866r0.pdf)에서 추가 되돌림이 예정됨

### 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](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p0429r9.pdf)로 `std::flat_map`과 `std::flat_set`을 추가했지만, `std::unordered_map`, `std::map`, `std::list` 자체의 기본 설계는 바꾸지 못함
- [multi-symbol order book benchmark](https://hftuniversity.com/post/eviscerated-by-rust-58x-stdlib-handicap)는 같은 워크로드, 같은 seed, 같은 격리 코어에서 C++의 `std::unordered_map`+`std::map`+`std::list`와 Rust의 `HashMap`+`BTreeMap`+`VecDeque`를 비교함

| 구현 | P99 cycles |
|---|---:|
| 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](https://hftuniversity.com/post/c26-shipped-a-simd-library-nobody)에서 같은 패턴의 대표 항목으로 다뤄지며, 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](https://peps.python.org/pep-0632/)로 `distutils`를 Python 3.12에서 제거했으며, [PEP 387](https://peps.python.org/pep-0387/)은 위험하거나 깨진 기능의 폐기 주기 단축 권한을 명시함
- Java는 Applet API를 Java 9에서 폐기 예정, Java 17에서 제거 예정, JEP 504에서 실제 제거까지 8년 경로로 처리했고, Nashorn은 [JEP 372](https://openjdk.org/jeps/372)로 Java 15에서 제거됨
- Java SecurityManager는 [JEP 411](https://openjdk.org/jeps/411)로 제거 예정 폐기 상태가 됐고 JEP 486으로 영구 비활성화됐으며, [JEP 398](https://openjdk.org/jeps/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&lt;bool&gt;`, `std::valarray`, `std::function` const-correctness 결함 같은 항목을 거의 제거하지 못하는 보존율임

### ABI 안정성이 만드는 영구 보존
- [P1863R1 “ABI - Now or Never”](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1863r1.pdf)는 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](https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html)와 [Itanium C++ ABI](https://itanium-cxx-abi.github.io/cxx-abi/abi.html) 같은 문서에서 구체화됨
- 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 라이브러리 같은 비표준 라이브러리에 크게 기대는 구조임

## Comments



### Comment 58995

- Author: dieafterwork
- Created: 2026-06-06T00:39:24+09:00
- Points: 1

원문이 고봉밥인데, 끝까지 읽다보니 rust 신봉 느낌이 많이 나긴하네요.  
그렇지만 몰랐던 정보도 많이 알아갔습니다. 좋은 글 감사합니다.

### Comment 58954

- Author: neo
- Created: 2026-06-05T09:04:45+09:00
- Points: 1

###### [Lobste.rs 의견들](https://lobste.rs/s/7jt1ix/c_standard_library_has_been_walking) 
- Rust 생태계에도 비슷한 churn이 있었는지 떠올려보면, 큰 건 몇 가지뿐인 듯함  
  **Leakpocalypse** 때 `Drop` 소멸자가 안전성 불변식을 지키기 위해 항상 실행된다고 의존할 수 없다는 결론이 났고, 실제 API 변화는 거의 없었으며 `std::thread::scoped` 제거 정도였음. 이후 같은 일을 sound하게 해주는 대체제가 생김  
  `std::mem::uninitialized`는 deprecated 되었고 지금은 unsound로 간주됨. 기존 `Range` 타입들은 비교적 작은 API 문제를 고치기 위해 거의 같은 새 타입으로 천천히 대체될 예정임. `std::error::Error::description`은 대부분의 오류 타입이 문자열을 저장하고 싶어 하지 않아서 deprecated 되었고, `Display` 구현이라는 직접 대체제가 있음  
  `std`가 11년 동안 안정적이었다는 점을 생각하면 꽤 놀랍고, 나머지 `std`는 여전히 존재하고 동작하며 98%는 아직도 관용적인 Rust로 여겨짐. 반면 **C++ 표준 라이브러리**는 기능 추가에는 너무 방아쇠가 가볍고, 어떤 상황에서도 deprecate에는 놀랄 만큼 보수적인 위험한 위치에 있어 보임
  - Leakpocalypse를 어떻게든 전혀 모르고 있었음: [faultlore (2015)](https://faultlore.com/blah/everyone-poops/)
  - `Iterator` 트레이트가 자기 내용물을 빌리는 문제도 떠오름. Rust 대화에서 “왜 이걸 못 쓰고 우회가 필요한가”로 계속 나오는 고질적 문제임  
    마찬가지로 `f32`와 `f64`가 `Cmp`를 구현하지 않고 대신 `f32::total_cmp` 메서드를 둔 것도 새 엔지니어들이 자주 부딪히는 성가신 부분이라, 한숨 쉬며 배경을 설명하게 됨  
    **panic 포맷팅 장치**도 별로 좋지 않고, 기본 panic handler가 포맷팅을 사용하며 끄기 어려워 실행 파일 크기를 꽤 잡아먹는다는 블로그 글이 많음

- 개인적으로는 **표준 라이브러리의 낡은 설계**가 C++의 인기와 사용성을 크게 깎아먹는다고 봄  
  언어 자체 탓으로 돌리는 많은 문제가 사실은 표준 라이브러리 쪽으로 향해야 함  
  예를 들어 “C++은 컴파일이 느리다”는 말은 정확하지 않음. C++ 기능을 쓴다고 본질적으로 느린 건 아니고, 대량의 헤더 비대화와 의존성, 단순한 추상화에도 템플릿을 과도하게 쓰는 표준 라이브러리가 느리게 만드는 것임  
  “C++은 안전하지 않다”도 일부는 맞지만, 표준 라이브러리 설계가 그걸 더 악화시킴. Rust API 설계에서 쓰는 더 안전한 패턴을 새 표준 라이브러리에 적용하지 못할 이유는 없음. 물론 C++의 큰 장점 중 하나가 **하위 호환성**이라 매우 복잡한 문제임
  - 몇몇 경우에는 맞음. `vec[idx]`가 범위 밖 접근/정의되지 않은 동작 대신 예외를 던지거나 abort하도록 만들 수는 있음. 하지만 언어 차이 때문에 C++에서 안전한 API를 만들기 훨씬 어려운 경우도 많음  
    Rust는 기본적으로 **파괴적 이동**이 있지만 C++은 아님. 그래서 스마트 포인터 API가 unsafe하거나, 최소한 놀랍고 crash-y해질 수밖에 없음. 예를 들어 moved-from 스마트 포인터 접근 시 프로그램을 abort하는 식임  
    Rust에는 수명 주석이 있지만 C++에는 없음. 그래서 Rust는 반복자 API 설계에서 반복자 무효화 같은 걸 막을 수 있지만 C++은 사실상 어렵다. Rust에는 패턴 매칭이 있어 `Option` 같은 API가 “확인하고 바로 쓰는” 접근을 ergonomically 제공할 수 있음. C++도 빈 값 접근 시 UB가 나지 않는 `std::option` 버전을 제공할 수는 있겠지만, 지금의 C++이나 Rust보다 훨씬 쓰기 귀찮을 것임. Rust의 `?` 연산자도 여기서 큰 도움이 됨  
    `std::variant`처럼 overload set으로 패턴 매칭 비슷한 걸 C++에 덧붙일 수 있다는 건 알지만, 훨씬 쓰기 어렵고 실수하기 쉽다고 봄
  - C도 마찬가지라고 봄. C의 많은 문제는 **stdlib가 형편없어서** 생김  
    문자열과 배열 라이브러리, 몇몇 제네릭 컨테이너, allocator에 대한 네이티브 지원을 갖춘 현대적인 라이브러리만 있어도 C를 훨씬 ergonomic하고 쓰기 쉽게 만들 수 있음. 물론 언어의 결함 일부는 라이브러리 교체만으로 사라지지 않지만, 그래도 꽤 멀리 갈 수 있음  
    현대적인 C 코드베이스들을 보면 allocator와 문자열, vector, 해시 테이블, 파일 시스템 작업용 커스텀 라이브러리를 폭넓게 쓰는데, C나 수동 자원 관리 경험이 있다면 그렇게 따라가기 어렵지 않음
  - 회사에서는 “정확히 N바이트를 가리키는 포인터”나 “임의 개수의 바이트를 가리키는 포인터”를 표현할 수 있는 `slice<T, N>` 구현을 쓰고 있음  
    `head(n)`, `tail(n)`, `slice(start, end)`, 인덱스 연산자가 있고 모두 **경계 검사**를 함  
    이런 추상화로 작업하는 건 정말 즐겁지만, 현대적이고 어느 정도 안전한 언어를 얻으려면 사실상 Rust와 Zig 표준 라이브러리를 C++로 포팅해야 함. 그래도 결국 들인 노력만큼 가치가 있음
  - 단순한 추상화에 템플릿을 덜 쓰기로 하면 **성능**을 잃는 것 아닌가?

- 이런 글을 쓸 거면 제발 직접 써줬으면 함. 목록은 직접 만들었을지 몰라도, 그걸 LLM에 넣고 결과를 웹페이지에 뿌려 사람이 읽게 하는 건 너무 무례함. “일하는 엔지니어”라면 “첫날”부터 “기능 X”를 피하라고 배운다는 문장을 한 번만 더 보면 미쳐버릴 것 같음  
  부끄러운 건 여기서 할 말이 정말 많은데 정작 아무 말도 안 하고 있다는 점임. 이 글을 만든 이유가 있을 테니 **그 이유를 말해줬으면 함**. C++의 어떤 부분 때문에 화가 났을 것이고, 어떤 기능에 혼란스러웠을 것임. 이 기능들이 나쁜 이유는 객관적인 설계 실패뿐 아니라 우리에게 미치는 영향 때문임  
  `std::iterator`를 썼다가 Slack에서 털린 적이 있는지, `reinterpret_cast`가 16글자라 줄 포맷이 살짝 나빠질까 봐 캐스트를 안 쓴 적이 있는지, 이런 것들이 Lobsters에 올라왔으면 더 좋겠음. 그런 이야기가 없다면 억지로 만들지 말고, GPU가 행렬 곱셈으로 같은 문장을 10번 만들게 하지 말았으면 함. 코멘트할 부분만 주석 달고, 나머지는 표와 bullet point로 쓰면 됨
  - 이 글이 LLM으로 쓰인 것처럼 읽히지는 않음

- C++을 20년 쓰고 지금도 쓰지만, 이 글에 많이 동의함. 요즘 Rust를 쓸 때 정말 좋은 건 메모리 안전성보다 **훌륭한 표준 라이브러리와 패키지 생태계**임  
  대표적인 예가 ranges 라이브러리임. 표준화된 지 6년이 지났는데도 주요 표준 라이브러리들이 아직 완전히 구현하지 못했고, 구현되더라도 조합자는 몇 개뿐임. Rust의 대응물인 `Iterator` 메서드는 76개이고, `cargo add` 한 번이면 `itertools` 트레이트로 130개가 더 생김  
  또 정말 그리운 건 패턴 매칭임. `std::variant` 같은 union 타입을 ergonomic하게 만들 수 있음. 제안은 논의 중이지만 C++26에도 아직 들어가지 않았고, 그건 아쉬움. 반면 contracts와 executors는 들어오는데 솔직히 주변에서 요청한 사람을 본 적이 없음
  - C++의 문제 중 하나는 어떤 기능이 언어 기능이어야 하는지, 표준 라이브러리 기능이어야 하는지에 대한 **공식적이고 문서화된 기준**이 없다는 점임  
    일반적으로 내가 보는 기준은 이렇다. 어떤 기능이 바람직한 사용 사례를 지원하고 표준 라이브러리로 표현할 수 없다면 언어에 들어가야 함. 가능하면 원하는 기능을 다른 목적에도 쓸 수 있는 최소한의 독립적인 요소로 분해해야 함  
    거의 모든 코드베이스에서 쓰이는 기능은 표준 라이브러리에 들어가야 함. 어떤 타입이 라이브러리 사이의 인터페이스로 흔히 쓰이면 표준 라이브러리에 들어가야 함. 모든 라이브러리가 자기만의 tuple 타입이나 문자열을 정의하게 만들고 싶지는 않음. C++은 전자는 C++11 전까지 사실상 그랬고, 후자는 `std::string`이 disaster라 아직도 그렇다. 이는 인터페이스 타입에도 적용되며, C++은 요즘 대부분 concepts로 처리함  
    나머지는 재사용 가능한 모듈형 라이브러리에 들어가야 함. Rust는 안정적이고 blessed된 외부 라이브러리 집합을 갖추는 데 꽤 좋기 때문에, “Rust로 쓰는 모든 게임에 이 자료구조가 필요하니 표준 라이브러리에 넣자”는 압력이 훨씬 작음. 게임을 쓰는 사람들은 필요한 crate를 가져오면 되기 때문임. C++은 “다수지만 과반은 아닌 사람들이 가진 문제에 추천할 좋은 패키지”라는 생각을 제대로 받아들인 적이 없음

- 걱정되는 건 **현재 추가 중인 것들** 중 무엇이 결국 나중에 되돌려질지임. Contracts가 C++26에 막 들어갔는데 벌써 심각한 설계 결함이 지적되고 있음  
  “위원회 설계”를 일반적으로 비난하고 싶지는 않음. 이런 조직들이 중요한 목적을 수행하고 고유한 강점이 있다고 생각함. 다만 그 강점은 완전히 새로운 기능을 초록밭에서 설계하는 데 있지 않음  
  WG21과 WG14가 정말 빛나는 지점은 설계 공간이 어느 정도 탐색되어 있고, 가능하면 여러 기존 구현이 있는 기능을 가져와서 사용자와 구현자 대부분이 받아들일 수 있는 표준 기능으로 만드는 일임. `std::embed`가 그런 예임  
  반대로 기사에 나온 GC 확장, `std::memory_order_consume`, C++20 modules처럼 누군가 구현을 제대로 해내기도 전에 표준화하면 일이 정말 나빠지는 경향이 있음
  - C++과 Haskell은 둘 다 위원회가 설계했지만, 두 언어는 거의 정반대에 가까움. “$X는 위원회가 설계했다”가 $X에 대해 뭔가를 함의한다고 생각하고 싶어질 때마다 이걸 떠올림

- 예전에 **C++이 표준 라이브러리를 버전 관리하지 않는다**는 걸 깨닫고 꽤 충격받았음. 이 글이 그 점을 콕 집을 줄은 몰랐음  
  Go가 forward compatibility에 비슷하게 보수적이라고 언급된 점도 흥미로움. 그런데 Go는 추가되는 기능에도 비슷하게 보수적이어서 C++의 문제 대부분을 피한 듯함. 안정 ABI가 없는 것도 도움이 됐을 것임  
  내가 아는 인기 라이브러리 중 명시적으로 C++ ABI를 노출하는 건 libcamera뿐인데, 꽤 성가심. 경험상 C++ 라이브러리도 보통 C ABI로 심볼을 내보내며, 그게 다른 언어와의 상호 운용도 쉽게 만듦. 내가 흐름을 놓친 것일 수도 있음  
  그리고 Clang과 MSVC 사이 ABI 호환성에는 quirks가 있지 않나? Conan이 컴파일러 섞기를 명시적으로 discouraging하거나 금지했던 기억이 있어서, C++ 위원회가 왜 ABI 안정성을 지키려고 애쓰는지 의문이 듦
  - 그건 완전히 맞지는 않음. C++은 표준 라이브러리를 **언어와 독립적으로** 버전 관리하지 않을 뿐임  
    여기에는 밀접하게 관련된 두 가지가 있음: 표준 라이브러리 명세와 구현임. 명세는 완전한 언어+라이브러리 조합에 대한 것이고, 구현은 보통 최소한 하나 이상의 명세 버전을 지원하려고 함  
    C++ 인터페이스를 노출하는 라이브러리는 많고, Qt처럼 매우 큰 것도 있음  
    문제는 C++ 추상 기계가 링크 과정을 정의하지 않는다는 점임. 그래서 동적 라이브러리가 어떻게 동작하는지 정의할 수 없음. UNIX 시스템의 C++ 동적 링크는 C 모델을 따름. 동적 링크인 척하고 loader 문제로 떠넘기는 식임. 이로 인해 copy relocation 같은 끔찍한 것들이 생김. Windows는 공유 라이브러리가 무엇인지에 대해 훨씬 원칙적인 개념을 갖지만, 그래서 UNIX C++ 라이브러리의 일부 idiom은 Windows에서 동작할 수 없음  
    공유 라이브러리는 C++ 템플릿 같은 기능에서 문제가 큼. 사용자 타입으로 템플릿을 instantiate할 수 있으려면, 컴파일러가 compilation-unit 경계를 볼 수 없기 때문에 전체 정의가 헤더에 있어야 함. 공유 라이브러리에서는 같은 코드가 여러 곳에서 instantiate됨. 프로그램과 라이브러리가 같은 매개변수로 같은 템플릿을 instantiate하면 둘 다 복사본을 갖게 되고, linker와 loader가 최종 로드된 프로그램에서 하나만 쓰이게 해야 함  
    Swift와 비교하면, Swift는 “공유 라이브러리는 존재하며, 이를 표현하는 언어 수준 구조를 노출한다”고 명시함. 공유 라이브러리 경계를 넘어 제네릭을 노출하고 싶으면 가능하지만, 모든 외부 호출자에게는 동적 디스패치 버전으로 낮춰짐. C++에서도 손으로 구현은 가능함. 타입 소거 wrapper를 쓰는 일반 버전의 템플릿을 만들고, 다른 구체 instantiation을 명시적으로 쓰면 됨. 하지만 이건 어렵고 수동적임. Swift에서는 그냥 “공유 라이브러리 경계에서 이렇게 된다”임  
    타입 숨기기도 마찬가지임. C++은 라이브러리 경계 너머로 동작은 노출하지만 구현은 숨기는 public interface를 만들기 위해 `pImpl` 패턴을 씀. Swift는 라이브러리 경계가 어디인지 아는 추상 기계를 갖고, “ABI-stable로 명시되지 않은 타입의 크기는 공유 라이브러리 경계 너머에서 컴파일 타임 상수가 아니다”라고 말함  
    표준이 현실을 부정하는 또 다른 형태도 있음. 내가 작업한 거의 모든 non-trivial C++ 코드베이스는 `-fno-rtti -fno-exceptions` 또는 CL.EXE의 같은 옵션으로 컴파일됐음. 표준은 이를 가능성으로 인정하지 않음. 대부분의 표준 라이브러리 함수는 여전히 오류 보고에 예외를 기대하므로 `-fno-exception`으로 컴파일하면 그냥 `abort`를 호출함. 그래서 동적 메모리 할당을 하는 표준 라이브러리 요소는 임베디드에서 쓸 수 없게 됨. `std::vector&lt;T&gt;::push_back`이 프로그램을 crash시킬 수 있음  
    기사에서 “위원회가 나쁜 기능을 제거하지 못할 뿐 아니라, 실무 엔지니어가 요청하지 않은 새 기능을 계속 추가한다”는 대목은 contracts가 생긴 방식과 100% 같음. [Verus](https://github.com/verus-lang/verus)는 C++과 같은 환경을 겨냥한 언어에서 좋은 contracts 시스템이 무엇을 가능하게 하는지 보여줌. P2900 contracts는 서로 충돌하는 요구사항의 조합이라, contracts가 맞을 수도 있는 모든 문제를 더 나쁘게 만듦  
    “C++ engineer”가 “프로그래밍할 수 있는 engineer”보다 훨씬 높은 임금을 받는다는 결론은 사실이 아니라고 봄. 실제로는 아무도 C++ 표준 그대로 코드를 쓰지 않고, 각자가 좋아하는 사내 subset-of-a-superset에 맞춰 쓰기 때문임
  - 여기서는 `go vet`도 가치가 있음. **API 개선을 위한 자동 업그레이드**를 제공하기 때문임

- 작년부터 C++은 거의 버렸고, 처음엔 Kotlin, 그다음엔 Swift로 옮겼음. 회사에서 아직 C++ 유지보수는 해야 하지만 새로 쓰는 코드는 훨씬 **깔끔하고 간결하며 안전함**. 코드 크기와 어쩌면 성능에서 tradeoff를 하고 있지만 그만한 가치가 있음

- Go의 for loop 의미가 하위 호환성을 깨며 바뀐 걸 기억해서 이 문장이 틀렸다고 생각했음: https://go.dev/blog/loopvar-preview  
  그런데 알고 보니 Go도 여기서 Rust editions와 비슷한 방식을 씀. Go 버전을 1.22 이상으로 선언해야 의미가 바뀜. 아마 `io/ioutil`도 이런 식으로 제거할 수는 있겠지만, edition 경계를 넘어서까지 코드를 깨뜨릴 만큼 가치가 있지는 않은 듯함

- C++이 이런 나쁜 아이디어들을 실제로 시도해보고 나쁜 아이디어임을 증명하지 않았다면 Rust는 지금 형태로 존재하지 못했을지도 모름. Big Thank You!

- C++용 **Rust식 표준 라이브러리 대체재**에 관심이 있음. 그걸 목표로 하는 rpp는 알고 있음: https://github.com/TheNumbat/rpp  
  다른 선택지가 있을까? EASTL 같은 C++ stdlib의 다른 구현 말고, Rust를 더 가깝게 따르는 라이브러리를 말하는 것임. `std::initializer_list` 같은 일부는 문법에 박혀 있다는 건 알지만, 나머지는 전부 바꿀 수 있음
