1 ポイント 投稿者 GN⁺ 18 시간 전 | 2件のコメント | WhatsAppで共有
  • C++標準ライブラリはC++11以降、誤った設計を正式に廃止したり、新しい代替機能の横に放置したりすることを繰り返しており、開発者は「使うべきではない層」がどの時期のものかを把握しなければならない構造になっている
  • 公式の撤回層には、std::auto_ptr、動的例外指定、C++11ガベージコレクションインターフェース、std::aligned_storageのように廃止・削除の論文が付いた項目があり、std::functionstd::move_only_functionstd::copyable_functionstd::function_refに囲まれた15年がかりの代替の流れの中に置かれている
  • 非公式な回避層には、遅いstd::regex、デストラクタで待機してデッドロックの罠を作るstd::async<iostream>std::liststd::dequestd::vector<bool>のように、標準には残っているがプロダクションコードでは迂回される機能が並ぶ
  • 基本コンテナの問題はstd::unordered_mapstd::mapstd::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::functionconst operator()には、non-constな呼び出し可能オブジェクトを呼び出してしまうconst-correctness上の欠陥があり、ABIを壊さずには修正できない状態である
  • この欠陥への対応として、std::move_only_functionはC++23のP0288R9std::copyable_functionはC++26のP2548R6std::function_refはC++26のP0792R14という流れにある

公式に巻き戻された標準機能

  • std::auto_ptrはコピー移動の意味論がジェネリックコードと標準コンテナを壊すため、C++11で非推奨となり、C++17でN4190によって削除された。同じ論文ではC++98の<functional>アダプタ群とstd::random_shuffleも削除している
  • std::random_shufflestd::randとグローバル状態に依存していたため、std::shuffleに置き換えられた
  • 動的例外指定throw(X, Y)はC++11で非推奨となり、C++17でP0003R5により削除され、throw()エイリアスはC++20のP1152で削除された
  • std::iteratorはC++17でP0174R2により非推奨となり、C++26での削除がP3365R1で進められており、代替手段は5つのtypedefを自分で定義することだ
  • std::aligned_storagestd::aligned_unionはC++11で導入された後、C++23のP1413R3で非推奨となった。typename ::typeのボイラープレート、reinterpret_castLen == 0での未定義動作、constexpr非対応が問題として挙げられている
  • std::not1std::not2unary_negatebinary_negateはC++17で非推奨、C++20で削除され、P0005std::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::printstd::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>が代表的な分析で、ビット詰め格納自体は有用だが、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_mapankerl::unordered_denseなども同様の方向の代替であり、RustのHashMapはhashbrownのSwissTable移植を標準ライブラリのデフォルトとして使っている
  • std::mapstd::setはノードベースのred-black treeであるため、ノードごとにヒープ確保が必要で、走査のたびにポインタ追跡が発生する。Abseil btree_mapとRust BTreeMapはB-treeベースで同じ問題を避けている
  • C++23はP0429R9std::flat_mapstd::flat_setを追加したが、std::unordered_mapstd::mapstd::list自体の基本設計は変えられない
  • multi-symbol order book benchmarkは、同じワークロード、同じ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のために3つのデフォルトを直せないという点にある

Vasa問題と機能の蓄積

  • Bjarne Stroustrupの2018年WG21文書P0977R0 “Remember the Vasa!”は、1628年に沈没したスウェーデンの戦艦Vasaを比喩に、委員会には「約150人の料理人」がいて、個々の機能がシステム全体に与える影響を十分に扱えていないと診断している
  • std::simdstd::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の3実装は、それぞれ一桁人数規模のエンジニアチームで保守されており、新しい標準機能はテストマトリクス、適合性バグ、機能間相互作用、次の保守担当者へ引き継がれるバグトラッカー項目を増やしていく
  • std::regexは15年にわたり既知の問題を抱え、std::dequeには再設計が必要というイシューがあり、C++20 modulesは標準化から6年後でも3実装すべてで綺麗に動く状態ではないと描写されている
  • 現代C++標準の実運用知識は、どの時期の誤った層なのか、どのサードパーティ回避策を使うべきか、3つの標準ライブラリ実装の違い、理論と実務の差分を理解した少数の専業エキスパートに集中する構造になっている

他の言語との違い: 過ちの有無ではなく保存率

  • PythonはPEP 594で20以上の標準ライブラリモジュールを削除し、PEP 632distutilsを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::uninitializedMaybeUninitstd::error::Error::descriptionsourcetry!マクロは?演算子に置き換えられた
  • C#は.NET Frameworkから.NET Coreへ移行する中で、BinaryFormatter、AppDomains、Remoting、Code Access Security、WCF server、WebFormsを切り捨てるメジャーバージョン移行を受け入れた
  • JavaScriptはWeb互換性の制約からほとんど削除しないが、cancelable promisesはStage 1で撤回され、SIMD.jsはWebAssembly SIMDへと捨てられ、GoはGo 1互換性の約束のためio/ioutilを非推奨のまま残す道を選んでいる
  • C++の違いは、過ちを犯したかどうかではなく、std::regexstd::unordered_mapstd::vector<bool>std::valarraystd::functionのconst-correctness欠陥のような項目をほとんど削除できない保存率にある

ABI安定性が生む永久保存

  • P1863R1 “ABI - Now or Never”は、C++23でABI breakを受け入れるか、永久ABI安定性を選ぶかを問う流れだったが、委員会は事実上、永久ABI安定性を選んだ
  • この選択のため、std::regexの修正、std::unordered_mapのopen addressing化、std::liststd::mapstd::dequeの構造変更が難しくなっている
  • C++標準ライブラリABIは動的リンカによって強制され、あるlibstdc++リリースでコンパイルされたオブジェクトが別リリースのオブジェクトとリンクできなければならないため、std::stringレイアウトやstd::regex_traits構成のような詳細が配布バイナリに固定される
  • この制約はlibstdc++ ABI policyItanium C++ ABIのような文書で具体化されている
  • Python利用者はpython==3.12、Rust利用者はCargo.tomlのエディション、Java利用者はJDKバージョン、C#利用者はnet6.0net8.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ライブラリのような非標準ライブラリに大きく依存する構造になっている

2件のコメント

 
dieafterwork 3 시간 전

原文はかなり山盛りですが、最後まで読んでみると Rust 信奉っぽい雰囲気もかなりありますね。
それでも、知らなかった情報もたくさん知ることができました。良い記事をありがとうございます。

 
Lobste.rsの意見
  • 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)
    • Iterator トレイトが自身の中身を借用する問題も思い出す。Rust の議論で「なぜこれが使えず回避策が必要なのか」と繰り返し出てくる持病のような問題だ
      同様に、f32f64Cmp を実装せず、代わりに f32::total_cmp メソッドを持っているのも、新しいエンジニアがよくぶつかる厄介な点で、ため息をつきながら背景を説明することになる
      panic フォーマット機構 もあまり良くなく、デフォルトの panic handler がフォーマットを使い、無効化しづらいため実行ファイルサイズをかなり食う、というブログ記事も多い
  • 個人的には、標準ライブラリの古い設計 が C++ の人気と使いやすさを大きく損ねていると思う
    言語そのもののせいにされる多くの問題は、実際には標準ライブラリの側に向けられるべきだ
    たとえば「C++ はコンパイルが遅い」という言い方は正確ではない。C++ の機能を使うこと自体が本質的に遅いわけではなく、膨大なヘッダの肥大化と依存関係、単純な抽象化にもテンプレートを過剰に使う標準ライブラリが遅くしているのだ
    「C++ は安全ではない」も一部は正しいが、標準ライブラリの設計がそれをさらに悪化させている。Rust の API 設計で使われる、より安全なパターンを新しい標準ライブラリに適用できない理由はない。もちろん C++ の大きな強みの一つが 後方互換性 なので、非常に複雑な問題ではある

    • いくつかのケースではその通りだ。vec[idx] を範囲外アクセス/未定義動作ではなく、例外を投げるか abort するようにすることはできる。しかし言語の違いのせいで、C++ では安全な API を作るのがずっと難しい場合も多い
      Rust には基本的に 破壊的ムーブ があるが、C++ にはない。だからスマートポインタ API は unsafe になるか、少なくとも驚くような、そして crash しやすいものにならざるを得ない。たとえば 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 を1回実行すれば 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<T>::push_back がプログラムを crash させうる
      記事中の「委員会は悪い機能を取り除けないだけでなく、現場のエンジニアが求めていない新機能を追加し続けている」というくだりは、contracts が生まれた経緯と 100% 同じだ。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 のように文法に埋め込まれているものが一部あるのは分かっているが、それ以外は全部置き換えられる