web-dev-qa-db-ja.com

ほとんどのSTL実装のコードが非常に複雑なのはなぜですか?

STLはC++の世界の重要な部分であり、ほとんどの実装はStepanovとMusserによる最初の取り組みから派生しています。

私の質問はコードの重要性を与えられており、それは人々が畏敬の念と学習の両方の目的でよく書かれたC++の例を見るための主要な情報源の1つです:なぜSTLのさまざまな実装を見るのがとても嫌なのですか?美学の観点からC++コードを書かない方法の一般的に良い例。

以下のコード例は、変数の命名、レイアウト、マクロ、および実際に何が起こっているのかを理解するために一目見ただけでは不十分な演算子の使用など、さまざまな理由で、私が働いた場所でのコードレビューに合格しませんでした。

template<class _BidIt> inline
bool _Next_permutation(_BidIt _First, _BidIt _Last)
{  // permute and test for pure ascending, using operator<
_BidIt _Next = _Last;
if (_First == _Last || _First == --_Next)
   return (false);

for (; ; )
   {  // find rightmost element smaller than successor
   _BidIt _Next1 = _Next;
   if (_DEBUG_LT(*--_Next, *_Next1))
      {  // swap with rightmost element that's smaller, flip suffix
      _BidIt _Mid = _Last;
      for (; !_DEBUG_LT(*_Next, *--_Mid); )
         ;
      _STD iter_swap(_Next, _Mid);
      _STD reverse(_Next1, _Last);
      return (true);
      }

   if (_Next == _First)
      {  // pure descending, flip all
      _STD reverse(_First, _Last);
      return (false);
      }
   }
}


_Ty operator()()
   {  // return next value
   static _Ty _Zero = 0;   // to quiet diagnostics
   _Ty _Divisor = (_Ty)_Mx;

   _Prev = _Mx ? ((_Ity)_Ax * _Prev + (_Ty)_Cx) % _Divisor
      : ((_Ity)_Ax * _Prev + (_Ty)_Cx);
   if (_Prev < _Zero)
      _Prev += (_Ty)_Mx;
   return (_Prev);
   }

それは非常によく設計されており、適用可能であるため、私はインターフェースを批評していないことに注意してください。私が懸念しているのは、実装の詳細の読みやすさです。

同様の質問が以前に提起されました:

STLの読み取り可能な実装はありますか

STLの実装がとても読めないのはなぜですか?ここでC++をどのように改善できたでしょうか?

注:上記のコードは、MSVC2010アルゴリズムとキューヘッダーから取得されています。

65
Matthieu N.

現在「anon」としてリストされているNeilButterworthは、SO質問 "STLの読み取り可能な実装はありますか?" への回答に役立つリンクを提供しました。引用そこでの彼の答え:

元のSTLデザイナーであるStepanov&Lee(PJPlaugerとDavidMusserと共に)が共同執筆したC++ Standard Template Libraryという本があり、コードを備えた実装の可能性について説明しています。 http:// www.Amazon.co.uk/C-Standard-Template-Library/dp/0134376331

そのスレッドの他の回答も参照してください。

とにかく、ほとんどのSTLコード(ここではSTLとはC++標準ライブラリのSTLのようなサブセットを意味します)はテンプレートコードであるため、ヘッダーのみである必要があります。ほとんどすべてのプログラムで使用されているため、できるだけ短いコード。

したがって、簡潔さと読みやすさの間の自然なトレードオフポイントは、「通常の」コードの場合よりも、スケールの簡潔さの端ではるかに大きくなります。

さらに、標準ライブラリは、アプリケーションコードのシステムに依存しないビューが基盤となるシステムに接続される場所であり、アプリケーション開発者として避けるべきあらゆる種類のコンパイラ固有のものを利用します。

19

変数名については、ライブラリの実装者は、アンダースコアで始まり大文字が続く名前など、「クレイジー」な命名規則を使用する必要があります。このような名前は変数名のために予約されているためです。 「通常の」名前はユーザーマクロによって再定義されている可能性があるため、使用できません。

セクション17.6.3.3.2「グローバル名」§1は次のように述べています。

名前と関数シグネチャの特定のセットは、常に実装用に予約されています。

  • 二重下線を含む、または下線で始まり大文字が続く各名前は、あらゆる用途のために実装用に予約されています。

  • アンダースコアで始まる各名前は、グローバル名前空間で名前として使用するために実装用に予約されています。

(これらのルールは、私がよく見かける__MY_FILE_Hのようなヘッダーガードを禁止していることに注意してください。)

18
fredoverflow

これが標準ライブラリコードであるための変数名。ヘッダーの実装の詳細には予約名を使用する必要があります。以下はnot標準ライブラリを破るはずです:

_#define mid
#include <algorithm>
_

したがって、標準ライブラリヘッダーは変数名としてmidを使用できないため、__Mid_になります。 STLは異なっていました-それは言語仕様の一部ではなく、「ここにいくつかのヘッダーがあります、あなたがそうするようにそれらを使用してください」と定義されました

一方、あなたのコードまたは私のコードは、変数名として__Mid_を使用した場合、それは予約済みの名前であるため無効になります。実装では次のことが許可されています。

_#define _Mid
_

それがそれのように感じるならば。

レイアウト-まあ。彼らはおそらくスタイルガイドを持っているでしょう、彼らはおそらくそれに従います、多かれ少なかれ。それが私のスタイルガイドと一致しない(したがって私のコードレビューに失敗する)という事実は彼らにとって何の意味もありません。

解決するのが難しいオペレーター-誰にとって難しいですか?コードはそれを維持する人々のために書かれるべきであり、GNU/Dinkumware /おそらく一目で_*--_Next_を解き明かすことができない標準ライブラリで人々を解放したくない人のために書かれるべきです。そのような表現を使うと慣れますし、使わないとなかなか見つけられません。

ただし、operator()のオーバーロードは意味不明です。 [編集:わかりました。これは線形合同法であり、非常に一般的に実行されます。モジュラスが「0」の場合は、算術型の自然なラップアラウンドを使用することを意味します。]

11
Steve Jessop

実装は異なります。 libc ++ たとえば、目にははるかに簡単です。ただし、アンダースコアノイズはまだ少しあります。他の人が指摘しているように、残念ながら先頭の下線が必要です。 libc ++の同じ関数は次のとおりです。

template <class _Compare, class _BidirectionalIterator>
bool
__next_permutation(_BidirectionalIterator __first, _BidirectionalIterator __last, _Compare __comp)
{
    _BidirectionalIterator __i = __last;
    if (__first == __last || __first == --__i)
        return false;
    while (true)
    {
        _BidirectionalIterator __ip1 = __i;
        if (__comp(*--__i, *__ip1))
        {
            _BidirectionalIterator __j = __last;
            while (!__comp(*__i, *--__j))
                ;
            swap(*__i, *__j);
            _STD::reverse(__ip1, __last);
            return true;
        }
        if (__i == __first)
        {
            _STD::reverse(__first, __last);
            return false;
        }
    }
}
4
ergosys

その理由の一部は、STLのコードが高度に最適化されていることだと思います。実装されているコードの種類では、読みやすさよりもパフォーマンスの方がはるかに重要です。それらは非常に広く使用されているため、できるだけ速くすることは理にかなっています。

2
Winston Ewert

人々がすでに言ったことを追加すると、あなたが見るスタイルはGNUスタイルです。醜いですか?おそらく、それは見る人の目にあります。しかし、それは厳密に定義されたスタイルであり、実際にそうです。慣れることに抵抗するのではなく、すべてのコードを同じように見せます。

0
wilhelmtell