C++ 標準ライブラリに含めるべきものは何ですか?

Meeting C++ 2017 で — ところで、それは素晴らしいものでした — 私は Guy Davidson による C++ グラフィックス 2D の提案についての講演に参加しました。

現在、この提案についていくつかの論争があります — 特に本格的なグラフィック処理を行う人たちの間で.C++ 標準ライブラリは 2D グラフィックスを必要としますか?深刻なアプリケーション?

しかし、私は愚かな標準委員会や完全に肥大化して使用できない標準ライブラリについて怒鳴るためにここにいるわけではありません.代わりに、この議論は私に考えさせました:言語の標準ライブラリの一部になりますか?

完璧な世界を想像してみてください

C++ プログラマーに完璧な世界を想像するように頼むと、C++ 依存関係のインストールがまったく簡単にできる世界を思い浮かべる可能性があります。高品質で十分に文書化された外部ライブラリを取得することがまったく問題ない世界.

そのような世界では、標準ライブラリはそもそも必要ですか? ?この優れた外部ライブラリ コードをすべてに使用できないでしょうか?

答えはノーです。いくつかの理由があります。

1.コンパイラマジック

一部の標準ライブラリ機能は、通常のプログラマーでは実装できません。 std::initializer_list を考えてみてください。 .これは、ブレース付きイニシャライザを記述するときにコンパイラ マジックによって呼び出されるマジック タイプです。自分で実装することはできません。

もう 1 つの例は、コンストラクターを呼び出すプレースメント new 演算子です。自分でコンストラクターを呼び出すことはできません。

さて、あなたはそのような魔法が好きではないかもしれません.そして私もそれが好きではありません.

しかし、可能なこともあります 実装するのは難しい - 特にポータブルにしたい場合. std::abort() を考えてください .または std::memcpy() .後者は自分で行うこともできますが、標準ライブラリ関数の速度に匹敵するのは非常に困難です.したがって、コンパイラにそれらを行うように依頼することは理にかなっています.

または、 std::atomic と書かなければならないことを想像してみてください !

最後に、コンパイラは、これまでよりもはるかに優れた仕事をすることがあります.

std::make_index_sequenceを取る たとえば、上限を取り、std::index_sequence を生成します。 0 からの数字が与えられた場合 上限に排他的です.So std::make_index_sequence<5> std::index_sequence<0, 1, 2, 3, 4> を生成します .これは、一部のメタ プログラミング アプリケーション、特に折り畳み前の式に役立ちます。

単純な実装は次のようになります:

template <std::size_t N, std::size_t ... SequenceSoFar>
struct make_sequence_impl
{
  using type = typename make_sequence_impl<N - 1, N - 1, SequenceSoFar...>::type;
};

template <std::size_t ... SequenceSoFar>
struct make_sequence_impl<0, SequenceSoFar...>
{
  using type = index_sequence<SequenceSoFar...>;
};

template <std::size_t N>
using make_index_sequence = typename make_sequence_impl<N>::type;

この素朴な実装には O(n) があります make_sequence_impl のインスタンス化 .あまり単純でない実装では、O(log n) まで下げることができます ただし、コンパイラに単に「ねえ、N 個の整数のシーケンスを生成してください」と頼むと、テンプレートのインスタンス化なしでそれを実行できます.大規模なテンプレート メタ プログラミングを行う場合、この速度は重要です.

他のいくつかの TMP 処理はそのように行われ、一部の <type_traits> は たとえば、SFINAE または特殊化ベースの実装は遅すぎるため、ある型が別の型の基底クラスであるかどうかをコンパイラに問い合わせてください。

2.語彙の種類と概念

しかし、物事を標準ライブラリに入れる理由は他にもあります。

私が以前に夢見た完璧な世界のシナリオについてもっと考えてみましょう.外部ライブラリを取得するのが簡単な場合、誰もが取得します.そして、これは複数のライブラリを使用しようとするときに問題になる可能性があります.

たとえば、文字列を読み取り、その文字列が特定の長さになるまで左側に特定の文字を埋め込みたい場合があります。当然のことながら、読み取り用と左埋め込み用の 2 つの外部ライブラリを使用します。どちらも非常に難しい作業です。

どちらのライブラリも文字列を操作する必要があります。特に、両方とも動的にサイズ変更された文字列が必要なので、単純な const char* もちろん、どちらのライブラリも文字列を提供する外部ライブラリを使用していますが、同じものを使用していません!

I/O 入力ライブラリは、さまざまなエンコーディングなどについて非常に懸念していたため、foo::UnicodeAwareString を使用することにしました。 、巧みにデザインされた傑作です。

左パッド ライブラリは、「Unicode」などを考慮して文字列を左にパディングするという難しい作業に専念することになっていたため、bar::hopefully_ascii_string を使用しました。

文字列型を変換せずに 2 つのライブラリを一緒に使用することはできません!左パッドが Unicode をサポートしていないことを考えると、これは不可能です.これは左パッドの例の問題ではありませんが、ライブラリが別の文字列型を使用しました。

「語彙型」にはいくつかの型があります。それらは多くのインターフェイスで使用され、すべてのライブラリで同じである必要があります。そうでない場合、2 つを組み合わせるのは面倒です。文字列は語彙型の例ですが、オプションでもあります、単純な動的配列、およびスマート ポインター。

標準ライブラリがそれを提供しない場合、他のすべての人が独自の、わずかに異なるバージョンを作成します。すべてのオプションを見てください!

しかし、それだけではありません。「語彙の概念」もあります。

C++ 標準ライブラリは、拡張できるように設計されています。アルゴリズムは汎用的で、コンテナーは共通の設計で、アロケーターは同じです。これにより、同じインターフェイスを提供/要求する独自のコンテナーまたはアルゴリズムを記述でき、誰もがそれらを使用できます。標準ライブラリが定義する基本的な概念は、実際の具体的な実装と同じくらい重要ですが、それ以上に重要ではありません.

現実世界

しかし残念なことに、私たちは完璧な世界に住んでおらず、外部の依存関係は決して些細なものではありません.そのような世界では、物事を標準ライブラリに入れるもう1つの理由があります.

3.ただ便利です

すべての重要なプログラムは、なんらかの形式の I/O を実行する必要があります。

多くのプログラムは、何かを並べ替えたり検索したりする必要があります。

多くのプログラムは、重要な数学を必要とします.

多くのプログラムが文字列を操作します。

それらはどこにでもあります:標準ライブラリに入れるだけです. ほとんどのプログラムで使用されている場合は, それらを含める必要があります.

標準ライブラリに入れることの欠点

もちろん、どちらの決定も一方的なものではありません。標準ライブラリには欠点があります。いったんライブラリに組み込まれると、取り出したり、変更したり、修正したりすることはほとんど不可能です。

標準ストリームは、I/O を行うための美しい方法です。1998 年であれば、今では肥大化し、過度に複雑になっています。言語は進化しました。可変個引数テンプレートは、ビット シフトよりも優れたフォーマット ソリューションです。 OOP がすべての解決策ではないことを知り、OOP から遠ざかりました。今では UTF-8 が重要です。

しかし、ストリームはまだそこにあり、多くは変わっていません.std::string まだあります、std::vector<bool> まだあります。

さらに、すべての人を満足させることはまったく不可能です。すべての設計には多くのトレードオフがあります。

std::vector<T> のような単純なもの 小さいベクトルを最適化したい場合はどうすればよいでしょうか?要素を初期化せずにサイズを変更したい場合はどうすればよいでしょうか?上限サイズを固定したい場合はどうすればよいでしょうか?…

それでも重要なのは std::vector 標準ライブラリの一部です:T の動的配列をどのように定義するかを定義します 置き換えを書きたい場合は、それがどのように見えるべきかを知っており、汎用コードでもそれを処理できる方法で書くことができます。

結論

では、C++ には標準ライブラリの 2D グラフィックスが必要ですか?

これは確かにコンパイラの魔法を必要とせず、ほとんどのアプリケーションには役に立ちません。さらに、実際の深刻なユースケースには十分ではありません.

ただし、特定のボキャブラリ タイプを提供します。たとえば、2D ポイントです。単一のポイント クラスは、全員が独自のポイントをロールアップする代わりに、多くのライブラリに大きなメリットをもたらします。そうでなければ、ポイントを追加するには遅すぎます。この時点での語彙タイプ。

それでも、標準ライブラリに語彙の種類が増えることにはメリットがあると思います。それは単なる適応の問題です。