0

私は、C ++の非メンバー関数は、引数のタイプに基づいてディスパッチしないという(おそらく間違った)仮定の下にありました。しかし、それについて読んだ後iterator_category、引数のカテゴリタイプに応じて関数を呼び出すことができ、その呼び出しは継承も処理するようです。たとえば、ランダムアクセスイテレータと入力イテレータの実装のみを記述した場合、非ランダムアクセスイテレータを使用したすべての呼び出しは、入力イテレータを受け入れる関数に移動します。これは本からの短縮例です

template <class T>
foo(T a) {
  foo(a, iterator_traits<T>::iterator_category());
}

template <class T>
foo(T a, random_access_iterator_tag) { \\body}

template <class T>
foo(T a, input_iterator_tag) { \\body}

// presumably this works even for
// ostream_iterator<int> i(cout);
// foo(i);

この種のディスパッチは一般的に正しいですか、それともこれは特殊なケースですか?

たとえば、イテレータカテゴリベースの例で、ランダムアクセスイテレータと双方向イテレータのみの実装を指定した場合、コンパイラが出力イテレータがカバーされていないと文句を言った場合、コンパイラは実装が網羅的でない場合に警告することになっていますか?

また、オブジェクト/インスタンスではなく、型のみの引数を持つ関数に遭遇したのはこれが初めてです。では、その型のインスタンス/オブジェクトの名前を指定せずに、引数の1つとして組み込み型またはユーザー定義型の関数を定義できますか?

以下は、コンパイル時のポリモーフィズムを実現するためのCRTPの代替手段のようです。それは正しい解釈ですか

template<class T>
int foo(T a) {
  foo(a, a::type());
}

int foo(int a, base) { \\body }
int foo(int a, derived) { \\body }
4

3 に答える 3

3

オーバーロードされた関数呼び出しは、静的型の引数を介して解決されます(これは、メンバー関数と非メンバー関数の両方に当てはまります)。

それで:

class Base {};
class Derived : public Base {};

void foo(Base &b) {}
void foo(Derived &d) {}

Base b;
Derived d;
Base &r = d;

foo(b);  // Calls first overload
foo(d);  // Calls second overload
foo(r);  // Calls first overload

アップデート

したがって、新しいコードスニペットでは、引数は型ではなく、単に「匿名」です。変数名は関連付けられていません。

これはすべてコンパイル時に解決されます。 typedefです(テンプレート を介してiterator_traits<T>::iterator_category依存します)。はコンストラクターを呼び出して、「ダミー」引数として使用されているそのタイプの新しい一時オブジェクトを作成します。次に、コンパイラーはその呼び出しを正しいオーバーロードに解決します。この引数がダミーであることを考えると、関数内に変数名は必要ありません。Titerator_traits<T>iterator_traits<T>::iterator_category()

于 2011-09-14T10:29:58.000 に答える
2

はい、これはコンパイル時のポリモーフィズムを実現する方法です。すべてのタイプはコンパイラーに認識されており、それがオーバーロードを選択する方法です。

たとえば、ランダムアクセスイテレータと入力イテレータの実装のみを記述した場合、非ランダムアクセスイテレータを使用したすべての呼び出しは、入力イテレータを受け入れる関数に移動します。この種のディスパッチは一般的に正しいですか、それともこれは特殊なケースですか?

イテレータタグクラスが関連している限り(たとえばbidirectional_iterator_tag、から継承されてinput_iterator_tagいる)。

たとえば、イテレータカテゴリベースの例で、ランダムアクセスイテレータと双方向イテレータのみの実装を指定した場合、コンパイラが出力イテレータがカバーされていないと文句を言った場合、コンパイラは実装が網羅的でない場合に警告することになっていますか?

コンパイラは、コードが必要なことを実行するかどうかを知りません。ただし、サポートされていないイテレータタイプを使用して関数をインスタンス化しようとすると、エラーが発生します。

また、オブジェクト/インスタンスではなく、型のみの引数を持つ関数に遭遇したのはこれが初めてです。では、引数の1つとして、組み込み型またはユーザー定義型の関数を定義できますか?これは最後の引数である必要がありますか?

あなたの構文は間違っていると思います。オブジェクトは一般的に使用されます(ただし、タグクラスにはメンバーがないため、オブジェクトはそのタイプに対してのみ作成されます)。テンプレートの特殊化も使用できると思いますが、そうすると、イテレータカテゴリ間の関係を利用できなくなります(双方向イテレータは入力イテレータなど)。

構文が通常どのように見えるかのサンプル。

#include <cstdio>
#include <iterator>
#include <iostream>
#include <vector>
#include <list>

template <class Iter>
void foo_impl(Iter, std::random_access_iterator_tag)
{
    puts("Iter is random access");
}

//for other iterator categories
template <class Iter, class IterCategory>
void foo_impl(Iter, IterCategory)
{
   puts("Iter is other kind of iterator");
}

template <class Iter>
void foo(Iter it)
{
    //use iterator_traits, which will recognize pointers as random access iterators
    foo_impl(it, typename std::iterator_traits<Iter>::iterator_category()); 
}

int main()
{
    int* p = 0;
    std::vector<int>::iterator vec_it;
    std::list<int>::iterator list_it;
    std::ostream_iterator<int> os_it(std::cout);
    foo(p);
    foo(vec_it);
    foo(list_it);
    foo(os_it); 
}

以下は、コンパイル時のポリモーフィズムを実現するためのCRTPの代替手段のようです。それは正しい解釈ですか

私が誤解していなければ、テンプレートの最も些細な使用でさえ、コンパイル時のポリモーフィズムと考えることができます。また、この手法はCRTP(AFAIKは標準ライブラリでは使用されていません)よりも古いと思います。

于 2011-09-14T10:58:30.107 に答える
2

実際、オブジェクトの静的型に基づいてディスパッチするコンパイル時のポリモーフィズムを使用しています。

イテレータのカテゴリは、(イテレータ自体ではなく)継承によって連鎖されているため、次のようになります。

InputIterator <- ForwardIterator <- BidirectionalIterator <- RandomAccessIterator

(これOutputIteratorも必要ですが、ここでは関係ありません)

を使用iterator_traitsして、現在のイテレータに関連付けられているイテレータカテゴリを取得できます。ダミー値を作成すると、オーバーロード解決プロセスが開始されます。例として、3つのオーバーロードがあるとします。

template <class T>
foo(T a, std::random_access_iterator_tag const&);
  // beware of slicing (in general)

template <class T>
foo(T a, std::forward_iterator_tag const&);

template <class T>
foo(T a, std::input_iterator_tag const&);

ここfooで、リストイテレータで使用するとします。

list<int> myList;
foo(myList.begin());

次に、fooスコープ(名前解決)に4が見つかります。

  • foo(T)すぐに破棄されます(引数の数が間違っています)
  • foo(T, std::random_access_iterator_tag const&)std::bidirectional_iterator_tagからへの変換がないため、は破棄されstd::random_access_iterator_tagます。

これにより、2fooは両方とも互換性があります(注:OutputIteratorを使用した場合、何も残っておらず、この時点でコンパイルエラーが発生します)。

次に、最終的に過負荷解決プロセスのランキング部分に入ります。はよりstd::forward_iterator_tagも「近い」(より直接的な)ベースstd::input_iterator_tagであるため、上位にランク付けされます。

foo(T, std::forward_iterator_tag const&)が選択されています。


ただし、これの静的な部分に注意してください。

std::forward_iterator_tag const& tag = std::vector<int>::iterator_category;
foo(myVector.begin(), tag);

tagここでは、実際には(動的)aであるにもかかわらず、std::random_access_iterator_tagシステムによってはとしてstd::forward_iterator_tag認識されるため、上記と同じ過負荷が選択されます。

于 2011-09-14T11:58:44.047 に答える