C ++イテレーター、すべてのイテレーターが継承するイテレーター基本クラスがないのはなぜですか


11

私は試験に向けて学習しており、質問と回答に苦労しています。

なぜ他のすべてのイテレータが継承するイテレータ基本クラスが存在しないのですか?

私の先生はcppリファレンス " http://prntscr.com/mgj542 " の階層構造を参照していると思います。なぜそれらを使用する必要があるのか​​、他の理由を提供する必要がありますか?

イテレータの種類(種類)と、コンテナの操作に使用されることを知っています。たとえば、基盤となるデータ構造が異なる可能性があるため、配列にはランダムにアクセスできますが、リンクリストにはアクセスできないため、コンテナーごとに異なるイテレーターがあり、コンテナーごとに移動方法が異なります。

それらはおそらくコンテナに応じて特殊化されたテンプレートでしょう。


2
あなたの研究を共有することはすべての人を助けます。何を試したか、なぜそれがニーズを満たしていないかを教えてください。これは、時間をかけて自分自身を助けようとしたことを示しており、明白な答えを繰り返すことから私たちを救い、そして何よりも、より具体的で関連性のある答えを得るのに役立ちます。また、How to Ask
gnat

5
なぜ、何のイテレータ基本クラスは、他のすべてのイテレータが継承存在しないのか?」うーん...なぜなければならない 1がありますか?
Nicol Bolas

回答:


14

すべてのイテレーターが単一のイテレーター基本クラスから継承する必要がない理由を指摘する回答をすでに得ています。でもかなり遠いです。C ++の目標の1つは、実行時コストがゼロの抽象化です。

共通の基本クラスから継承するすべてのイテレータが機能し、基本クラスの仮想関数を使用してインターフェイスを定義し、派生クラスがそれらの仮想関数の実装を提供した場合、実質的なランタイムが追加される可能性があります(多くの場合)含まれる操作へのオーバーヘッド。

たとえば、継承と仮想関数を使用する単純なイテレータ階層について考えてみましょう。

template <class T>
class iterator_base { 
public:
    virtual T &operator*() = 0;
    virtual iterator_base &operator++() = 0;
    virtual bool operator==(iterator_base const &other) { return pos == other.pos; }
    virtual bool operator!=(iterator_base const &other) { return pos != other.pos; }
    iterator_base(T *pos) : pos(pos) {}
protected:
    T *pos;
};

template <class T>
class array_iterator : public iterator_base<T> {
public: 
    virtual T &operator*() override { return *pos; }
    virtual array_iterator &operator++() override { ++pos; return *this; }
    array_iterator(T *pos) : iterator_base(pos) {}
};

次に、簡単なテストを行います。

int main() { 
    char input[] = "asdfasdfasdfasdfasdfasdfasdfadsfasdqwerqwerqwerqrwertytyuiyuoiiuoThis is a stringy to search for something";
    using namespace std::chrono;

    auto start1 = high_resolution_clock::now();
    auto pos = std::find(std::begin(input), std::end(input), 'g');
    auto stop1 = high_resolution_clock::now();

    std::cout << *++pos << "\n";

    auto start2 = high_resolution_clock::now();
    auto pos2 = std::find(array_iterator(input), array_iterator(input+sizeof(input)), 'g');
    auto stop2 = high_resolution_clock::now();

    std::cout << *++pos2 << "\n";

    std::cout << "time1: " << duration_cast<nanoseconds>(stop1 - start1).count() << "ns\n";
    std::cout << "time2: " << duration_cast<nanoseconds>(stop2 - start2).count() << "ns\n";
}

[注:コンパイラーによっては、コンパイラーがイテレーターを受け入れるために、iterator_category、difference_type、参照などの定義など、もう少し作業が必要になる場合があります。]

そして出力は:

y
y
time1: 1833ns
time2: 2933ns

[もちろん、コードを実行すると、結果はこれらと正確には一致しません。]

したがって、この単純なケース(および約80の増分と比較のみ)の場合でも、単純な線形検索に約60%のオーバーヘッドを追加しました。特にイテレータが元々C ++に追加されていた場合、かなりのオーバーヘッドを持つデザインを受け入れなかった人もかなりいました。それらはおそらく標準化されていなかったでしょうし、たとえ標準化されていたとしても、実質的に誰もそれらを使用しませんでした。


7

違いは、かとは何か、どのように動作するです。

多くの言語はこの2つを融合させようとしますが、それらはまったく異なるものです。

方法とは何か、方法とは...

すべてが継承する場合object、いくつかの利点が発生します。たとえば、オブジェクトの任意の変数が任意の値を保持できます。しかし、摩擦でもあること、すべてがしなければならない(振る舞うのように)object(のような、と見てどのようなobject

だが:

  • オブジェクトに平等の意味のある定義がない場合はどうなりますか?
  • 意味のあるハッシュがない場合はどうなりますか?
  • オブジェクトを複製できないが、オブジェクトは複製できる場合はどうなりますか?

いずれのobjectタイプも、オブジェクトがすべての可能なインスタンスに共通性を提供しないため、本質的に役に立たなくなります。あるいは、いくつかの推定普遍的な性質の壊れた/靴・ホーンド/不条理定義が上で発見したオブジェクトが存在しますobjectどのprovies ほとんど落とし穴の数を除き、普遍的な行動を。

方法に縛られないものは

あるいは、WhatHowを別々に保つことができます。次に、いくつかの異なるタイプ(何もまったく何も共通していない)は、コラボレーターからどのように見えると同じようにすべて動作できます。この意味で、anのアイデアはIterator特定のwhatではなく、howです。具体的にどのようにあなたがまだ知らないときのものと対話するかどうあなたと対話しています。

Java(および同様のもの)は、インターフェースを使用することでこれへのアプローチを可能にします。この点に関するインターフェースは、通信の手段を記述し、暗黙的に通信のプロトコルとそれに続くアクションを記述します。どれでもどんな与えられたのであることを自分自身を宣言した方法は、それがプロトコルによって概説関連する通信やアクションをサポートしていることを述べています。これにより、どのコラボレーターがどのWhatを使用できるを正確に指定することで、どのコラボレーターもHowに依存し、困惑することがなくなります。

C ++(および同様の)では、ダックタイピングによってこれにアプローチできます。テンプレートは、特定のコンパイルコンテキスト内で、特定の方法でオブジェクトと対話できるという動作に従うことを、協調型が宣言しているかどうかを気にしません。これにより、C ++ポインター、および特定の演算子をオーバーライドするオブジェクトを同じコードで使用できます。同等と見なされるチェックリストを満たすため。

  • * a、a->、++ a、およびa ++->入力/転送反復子をサポート
  • * a、a->、++ a、a ++、-a、およびa--->双方向イテレーターをサポート

基礎となる型は、コンテナを反復する必要もありません。でもかまいません。さらに、一部のコラボレーターをさらに汎用的にするa++ことができます。関数が必要とするものを想像してください。イテレーターはそれを満たすことができ、ポインターも整数もできるので、オブジェクトを実装できますoperator++

仕様の過不足

両方のアプローチの問題は仕様の過不足です。

インターフェイスを使用するには、オブジェクトが特定の動作をサポートすることを宣言する必要があります。これは、作成者が最初からそれを埋め込む必要があることも意味します。彼らはそれを宣言しなかったので、これはカットを作らないものを引き起こします。これは、WhatHowを表すインターフェースである共通の祖先を持つことも意味します。これはの最初の問題に戻りobjectます。これにより、共同編集者は要件を過剰に指定すると同時に、一部のオブジェクトが宣言の欠如のために使用できなくなったり、期待される動作が十分に定義されていないために隠されたりします。

テンプレートを使用するには、コラボレーターが完全に未知のWhatと連携し、その相互作用を通じてHowを定義する必要があります。ある程度のコラボレーターの作成は難しくなります。コンパイルエラーを回避しながら通信プリミティブ(関数/フィールドなど)のWhatを分析する必要があるか、少なくとも、特定のWhatHowの要件に一致しない方法を指摘する必要があるためです。これにより、コラボレーターは指定されたWhatからの絶対的な最小値を要求できるようになり、使用されるものの最も広い範囲が許可されます。残念ながら、これには、特定の通信プリミティブを技術的に提供するオブジェクトの無意味な使用を許可するという欠点があります。どのように、ただし、あらゆる種類の悪いことが発生することを許可する暗黙のプロトコルに従わないでください。

イテレータ

この場合Iteratorどのようにそれは、相互作用の記述の省略形です。その説明に一致するものはすべて、定義上、Iteratorです。方法を知ることにより、一般的なアルゴリズムを記述し、アルゴリズムを機能させるために提供する必要のある「どのように特定の何を与える」の短いリストを得ることができます。そのリストはfunction / properties / etcであり、それらの実装では、アルゴリズムによって処理されている特定のWhatが考慮されます。


6

C ++は、ポリモーフィズムを実行するために(抽象)基本クラスを持つ必要がないためです。構造的なサブタイプと主なサブタイプがあります。

イテレータの特定のケースでは混乱しますが、以前の標準std::iteratorは(およそ)として定義されています。

template <class Category, class T, class Distance = std::ptrdiff_t, class Pointer = T*, class Reference = T&>
struct iterator {
    using iterator_category = Category;
    using value_type = T;
    using difference_type = Distance;
    using pointer = Pointer;
    using reference = Reference;
}

つまり、必要なメンバータイプの単なるプロバイダーとしてです。実行時の動作はなく、C ++ 17で廃止されました

クラステンプレートはクラスではないため、これが共通のベースになることはできません。各インスタンス化は他のインスタンス化から独立しています。



5

1つの理由は、イテレータがクラスのインスタンスである必要がないことです。たとえば、多くの場合、ポインターは完全に優れたイテレーターであり、それらはプリミティブであるため、何からも継承することはできません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.