13

「Vector」という名前の独自のバージョンのベクターを作成して、C++ を練習しています。とりわけ、fill コンストラクターと range コンストラクターの 2 つのコンストラクターがあります。それらの宣言は次のようになります。

template <typename Type> 
class Vector {
public:
    // fill constructor
    Vector(size_t num, const Type& cont);

    // range constructor
    template<typename InputIterator> Vector(InputIterator first, InputIterator last);

    /* 
    other members
    ......
    */
}

塗りつぶしコンストラクターは、コンテナーを num の val で塗りつぶします。範囲コンストラクターは、範囲 [first, last) 内のすべての値をコンテナーにコピーします。それらは、STL ベクターの 2 つのコンストラクターと同じであると想定されています。

それらの定義は次のとおりです。

//fill constructor 
template <typename Type> 
Vector<Type>::Vector(size_t num, const Type& cont){
    content = new Type[num];
    for (int i = 0; i < num; i ++)
        content[i] = cont;
    contentSize = contentCapacity = num;
}

// range constructor
template <typename Type> 
template<typename InputIterator>
Vector<Type>::Vector(InputIterator first, InputIterator last){
    this->content = new Type[last - first];

    int i = 0;
    for (InputIterator iitr = first; iitr != last; iitr ++, i ++)
    *(content + i) = *iitr;

    this->contentSize = this->contentCapacity = i;
}

しかし、それらを使用しようとすると、それらを区別するのに問題があります。例えば:

Vector<int> v1(3, 5);

このコード行では、それぞれが 5 である 3 つの要素を含む Vector を作成するつもりでした。これは当然のことながら、エラーを引き起こします。

もちろん、コードを次のように変更すると:

Vector<int> v1(size_t(3), 5);

すべて問題なく、fill コンストラクターが呼び出されます。しかし、それは明らかに直感的でユーザーフレンドリーではありません。

では、塗りつぶしコンストラクターを直感的に使用できる方法はありますか?

4

4 に答える 4

12

コンストラクターを明確にするために使用できますstd::enable_if(またはboost::enable_ifC ++ 11を使用しない場合)。

#include <iostream>
#include <type_traits>
#include <vector>
using namespace std;


template <typename Type> 
class Vector {
public:
    // fill constructor
    Vector(size_t num, const Type& cont)
    { 
        cout << "Fill constructor" << endl;
    }

    // range constructor
    template<typename InputIterator> Vector(InputIterator first, InputIterator last,
        typename std::enable_if<!std::is_integral<InputIterator>::value>::type* = 0)
    { 
        cout << "Range constructor" << endl;
    }

};

int main()
{
    Vector<int> v1(3, 5);
    std::vector<int> v2(3, 5);
    Vector<int> v3(v2.begin(), v2.end());
}


上記のプログラムは、最初に、型が整数型であるかどうか(したがって、イテレーターではないかどうか)をチェックして、fillコンストラクターを呼び出す必要があります。


ちなみに、範囲コンストラクターの実装では、std::distance(first, last)ではなくを使用する必要がありますlast - first-イテレータで演算子を明示的に使用すると、タイプに制限されますが、最も一般的なタイプのイテレータ RandomAccessIteratorをサポートする必要があります。InputIterator

于 2012-12-26T03:06:40.107 に答える
3

このstd::vector問題があるようです。

std::vector<int> v2(2,3);

選ぶ

template<class _Iter>
        vector(_Iter _First, _Iter _Last)

Visual C++ では、テンプレート化されていないケースにより近いはずですが..

編集:上記の関数は(正しく)構築を下の関数に委任します。私は完全に失われています..

template<class _Iter>
        void _Construct(_Iter _Count, _Iter _Val, _Int_iterator_tag)

編集 #2 ああ!:

どういうわけか、この以下の関数は、呼び出されるコンストラクターのバージョンを識別します。

template<class _Iter> inline
    typename iterator_traits<_Iter>::iterator_category
        _Iter_cat(const _Iter&)
    {   // return category from iterator argument
    typename iterator_traits<_Iter>::iterator_category _Cat;
    return (_Cat);
    }

上記の_Construct関数には、上記の関数によって返されるタグである 3 番目の変数に (少なくとも) 2 つのバージョンがオーバーロードされてい_Iter_catます。このカテゴリのタイプに基づいて、正しいオーバーロード_Constructが選択されます。

最終編集: iterator_traits<_Iter>多くの異なる一般的な種類に対してテンプレート化されているように見えるクラスであり、それぞれが適切な「カテゴリ」タイプを返します

解決策: 最初の引数の型のテンプレートの特殊化は、stdMS VC++ の場合、ライブラリがこの厄介な状況 (プリミティブ値型) を処理する方法のようです。おそらく、あなたはそれを調べて、それに倣うことができますか?

問題が発生するのは (私が思うに) プリミティブ値の型では、Typesize_t変数が似ているため、2 つの同一の型を持つテンプレート バージョンが選択されるためです。

于 2012-12-26T03:12:01.213 に答える
3

問題は、標準ライブラリの実装が直面する問題と同じです。それを解決するにはいくつかの方法があります。

  • (最初のパラメーターの代わりに) すべての整数型に対して、テンプレート以外のオーバーロードされたコンストラクターを細心の注意を払って提供できます。

  • SFINAE ベースの手法 ( などenable_if) を使用して、範囲コンストラクターが整数引数に選択されていないことを確認できます。

  • if( を使用して) 整数引数を検出した後、( を使用して) 実行時に範囲コンストラクターを分岐し、is_integral制御を適切な構築コードにリダイレクトできます。分岐条件はコンパイル時の値になります。つまり、コンパイラによってコンパイル時にコードが縮小される可能性があります。

  • 標準ライブラリ実装のバージョンをのぞき見して、それがどのように行われているかを確認するだけです (ただし、抽象 C++ 言語の観点からは、それらのアプローチが移植可能である必要はなく、有効である必要もありません)。

于 2012-12-26T03:28:16.237 に答える
2

このあいまいさが、初期のライブラリ実装者に問題を引き起こしました。それは「正しいことをする」効果と呼ばれます。私が知る限り、それを解決するにはSFINAEが必要です… それはその技術の最初のアプリケーションの 1 つだったのかもしれません。(一部のコンパイラは、コア言語内で解決策が見つかるまで、オーバーロード解決の内部をごまかしてハッキングしました。)

この号の標準仕様は、C++98 と C++03 の主な違いの 1 つです。C++11 から、§23.2.3:

14 この条項および条項 21 で定義されているすべてのシーケンス コンテナーについて:

— コンストラクターの場合

       template <class InputIterator>
       X(InputIterator first, InputIterator last,
         const allocator_type& alloc = allocator_type())

が入力反復子として修飾されない型 InputIterator で呼び出された場合、コンストラクターはオーバーロードの解決に参加しません。

15 型が入力反復子にならないことを実装が決定する範囲は規定されていません。

于 2012-12-26T03:12:33.013 に答える