3

問題の説明(教育目的): -STLコンテナ、、、およびで
機能するメソッドprintContainerを実装します。 vectorstackqueuedeque

解決策を作りましたが、コードが多すぎるので気に入らないです。
問題を解決するために私がしたこと:
1。操作のためにコンテナーからの統一されたインターフェースを期待する設計されたジェネリック関数:最後の要素の値を取得し、コンテナーからその要素を消去します

template <typename T>
void printContainer(T container)
{
    cout << " * * * * * * * * * * " << endl;
    cout << " operator printContainer(T container). Stack, queue, priority queue" 
         << endl;
    cout << typeid(container).name() << endl;

    while (!container.empty())
    {
            cout << top(container) << "    ";
            pop(container);
    }
    cout << endl;
    cout << " * * * * * * * * * * * " << endl;
}

コンテナごとに、統一されたインターフェイスを提供できる関数を実装しまし た(次のコードスニペットをリファクタリングしたい):

template <typename T>
typename vector<T>::value_type top(const vector<T>& v)
{
    return v.back();
}
template <typename T, typename Base>
typename stack<T, Base>::value_type top(const stack<T, Base>& s)
{
    return s.top();
}

template <typename T, typename Base>
typename queue<T, Base>::value_type top(const queue<T, Base>& q)
{
    return q.front();
}

template <typename T, typename Base>
typename priority_queue<T, Base>::value_type top(const priority_queue<T, 
                                                              Base>& pq)
{
    return pq.top();
}

template <typename T>
void pop(vector<T>& v)
{
    return v.pop_back();
}

template <typename T, typename Base>
void pop(stack<T, Base>& s)
{
    return s.pop();
}

template <typename T, typename Base>
void pop(queue<T, Base>& q)
{
    return q.pop();
}

template <typename T, typename Base>
void pop(priority_queue<T,Base>& pq)
{
    return pq.pop();
}

私はそれを次のようなものに置き換えたくありません:

template <typename T, typename Base, template<typename T, class Base, 
class ALL = std::allocator<T>> class container>
typename container<T,Base>::value_type top(container<T,Base>& c)
{
    if (typeid(container).name == typeid(vector<T,Base>))
        return c.back();
    if (typeid(container).name == typeid(queue<T,Base>))
        return c.front();
    else
        return c.top();
}

template <typename T, typename Base, template<typename T, class Base, 
class ALL = std::allocator<T>> class container>
typename container<T,Base>::value_type pop(container<T,Base>& c)
{
    if (typeid(container).name == typeid(vector<T,Base>))
        c.pop_back();
    else
        return c.pop();
}

しかし、それは機能しません、私は次のようなエラーが発生します:

Error   1   error C2784: 'container<T,Base>::value_type top(container<T,Base> &)' : could not deduce template argument for 'container<T,Base> &' from 'std::stack<_Ty>'

質問:
エラーを整理するためにテンプレートテンプレートパラメータで隣接を作成する必要があります。見落としているか、論理エラーが存在する可能性があります。
とにかく、どんな有用な情報も歓迎されます。
前もって感謝します!

アップデート:

//それが私が関数を呼び出そうとしている方法です

int arr[] = {1,2,3,4,5,6,7,8,9,0};
    stack<int> s(deque<int>(arr, arr + sizeof(arr) / sizeof(arr[0])));;
    queue<int> q(deque<int>(arr, arr + sizeof(arr) / sizeof(arr[0])));
    priority_queue<int> pq(arr, arr + sizeof(arr) / sizeof(arr[0]));
    printContainer(s);
    printContainer(q);
    printContainer(pq);
4

3 に答える 3

3

このソリューション:

template <typename T, typename Base, template<typename T, class Base, 
class ALL = std::allocator<T>> class container>
typename container<T,Base>::value_type top(container<T,Base>& c)
{
    if (typeid(container).name == typeid(vector<T,Base>))
        return c.back();
    if (typeid(container).name == typeid(queue<T,Base>))
        return c.front();
    else
        return c.top();
}

実行時のif()選択を実現するため、機能しません。つまり、ブランチの1つだけが評価され、すべてのコンテナで機能が提供されていない場合でも、すべてのブランチのコードをコンパイルする必要があります(例)。truetop()vector

説明のために、この簡単な例を考えてみましょう。

struct X { void foo() { } };
struct Y { void bar() { } };

template<bool b, typename T>
void f(T t)
{
    if (b)
    {
        t.foo();
    }
    else
    {
        t.bar();
    }
}

int main()
{
    X x;
    f<true>(x); // ERROR! bar() is not a member function of X 

    Y y;
    f<false>(y); // ERROR! foo() is not a member function of Y
}

ここでは、コンパイル時に既知のブール型テンプレート引数を関数に渡していますf()true入力がタイプの場合はパスしているため、 ;Xというメンバー関数をサポートしています。入力がタイプの場合foo()はパスしているため、と呼ばれるメンバー関数をサポートします。falseYbar()

選択はコンパイル時に既知のブール値で機能しますが、ステートメント自体は実行時に実行されます。コンパイラーは最初に、ステートメントのfalseブランチを含む関数全体をコンパイルする必要があります。if

あなたが探しているのはある種のstatic if構造ですが、残念ながらC++では利用できません。

ここでの従来のソリューションはオーバーロードに基づいており、実際には最初に提供したものと同じように見えます。

于 2013-03-24T15:29:54.180 に答える
2

私は逆にそれに来るでしょう。イテレータを使用するジェネリック関数を作成します。

template <class Iter>
void show_contents(Iter first, Iter last) {
    // whatever
}

次に、コンテナを受け取るジェネリック関数:

template <class Container>
void show_container(const Container& c) {
    show_contents(c.begin(), c.end());
}

queue次に、またはstack:の基礎となるコンテナを取得するためのハック

template <class C>
struct hack : public C {
    hack(const C& cc) : C(cc) { }
    typename C::Container::const_iterator begin() const {
        return this->c.begin();
    }

    typename C::Container::const_iterator end() const {
        return this->c.end();
    }
};

次に、これらのオブジェクトを作成してその内容を表示するための特殊化を定義します。

template <class T>
void show_container(const stack<T>& s) {
    hack<stack<T>> hack(s);
    show_contents(hack.begin(), hack.end());
}

template <class T>
void show_container(const queue<T>& q) {
    hack<stack<T>> hack(q);
    show_contents(hack.begin(), hack.end());
}
于 2013-03-24T20:00:49.967 に答える
1

Andyの答えはすでに良いものですが、実装について1つの改善点を追加したいと思います。オーバーロードでは、STLコンテナーがデフォルト以外である必要があるすべてのテンプレートパラメーターが許可されないため、より多くのコンテナーの特殊化をサポートするように改善できます。たとえば、コードを見てください。

template <typename T>
typename vector<T>::value_type top(const vector<T>& v)
{
    return v.back();
}

そして今それをの定義と比較してくださいstd::vector。クラステンプレートには、 2つのパラメータがありますstd::vector<T,Allocator=std::allocator<T>>std::vectorオーバーロードは、2番目のパラメーターがであるsのみを受け入れますstd::allocator<T>

コードに手動でパラメーターを追加することもできますが、より良い代替手段があります。可変個引数テンプレートです。std::vector次のコードを使用して、すべてのsの真の汎用バージョンを作成できます。

template <typename... Ts>
typename vector<Ts...>::value_type top(const vector<Ts...>& v)
{
    return v.back();
}

もちろん、他のすべてのコンテナーにも同じ手法を使用でき、コンテナーが持つテンプレートパラメーターの正確な数について心配する必要はありません。一部のコンテナには最大5つのテンプレートパラメータがあるため、可変個引数テンプレートを使用しない場合、これは非常に煩わしい場合があります。

注意点:古いコンパイラの中には、可変個引数バージョンが気に入らない場合があるため、すべてのパラメータを手動で繰り返す必要があります。

于 2013-03-24T22:41:59.907 に答える