2010-12-04 8 views
15

どのタイプのSTLコンテナでも、特定のテンプレート化タイプのイテレータのみを入力として受け取る関数を定義する方法を教えてください。たとえば、次のように特定のタイプの要素のANYコンテナでSTLイテレータを使用する関数

フォームの任意のイテレータstd::list<Unit*>::iteratorまたはstd::vector<Unit*>::iterator

私はちょうどstd::list<Unit*>::iteratorを取る関数を定義しますが、我々は異なるSTLコンテナに切り替えた場合、私は変更する必要がありますする必要はありません私のコード。

テンプレートなどでこれを行う方法はありますか?

+2

'std :: list'? STLコンテナの生ポインタ?ガー! :) –

答えて

14

boost::enable_ifなどのSFINAE構造を使用して、ネストされたtypedef iterator::value_typeが実際に適切なタイプであるかどうかを検証できます。

template<class T, class Iterator> 
typename boost::enable_if<boost::is_same<typename Iterator::value_type, T> >::type 
    f(Iterator i) 
{ 
    /* ... */ 
} 

int main() 
{ 
    std::list<int> l; 
    std::vector<int> v; 

    f<int>(l.begin()); // OK 
    f<int>(v.begin()); // OK 

    std::vector<float> v2; 
    f<int>(v2.begin()); /* Illegal */ 
} 

これは私がから「入力としてSTLコンテナのいずれかのタイプの反復子を取る関数が、唯一の特定のテンプレートタイプのものに」理解するものですが、私の解釈が間違っているかもしれません。

+0

これは私にとって非常に有益な答えでした。ありがとうございました! –

6

my_special_typeのコンテナでのみ繰り返し処理したいですか?その場合:

template <bool, typename T> 
struct enable_if; 

template <typename T> 
struct enable_if<true, T> 
{ 
    typedef T type; 
}; 

template <typename T, typename U> 
struct is_same 
{ 
    enum {value = false}; 
}; 

template <typename T> 
struct is_same<T, T> 
{ 
    enum {value = true}; 
}; 

template <typename Iter> 
typename enable_if<is_same<typename Iter::value_type, your_special_type>::value, 
        void>::type 
function(Iter begin, Iter end) 
{ 
    // ... 
} 
+0

面白い!しかし、関数が誤った型で使用されている場合はどうなりますか?これが簡単なのであれば申し訳ありません。私はまだテンプレートを学んでいます。 – RyanG

+0

@Ryan: 'enable_if'は、最初のテンプレートパラメータが' true'の場合にのみ 'type'メンバを持ちます。あなたが間違って使用する場合、あなたはコンパイルに失敗します。 – Bill

+0

@Ryan: 'enable_if'のおかげで不正な型の関数は存在しません:) – fredoverflow

5

テンプレートを使用するもう1つの方法は、条件が満たされない場合に静的アサーションをトリガーすることです。

#include <iterator> 
#include <boost/type_traits/is_same.hpp>  
#include <boost/static_assert.hpp> 

template <class Type, class Iter> 
void foo(Iter from, Iter to) 
{ 
    BOOST_STATIC_ASSERT((boost::is_same<typename std::iterator_traits<Iter>::value_type, Type>::value)); 
    //... 
} 

int main() 
{ 
    int arr[10]; 
    foo<int>(arr, arr + 10); //OK 
    foo<double>(arr, arr + 10); //triggers static assertion 
} 

テンプレートを削除したい場合は、タイプ消去を使用して「any_iterator」を書き込むこともできます。たとえば、この上のように:SFINAEに頼る既存の回答に加えてhttp://stlab.adobe.com/classadobe_1_1any__iterator.html

14

、単純な近似は単にイテレータとして任意のテンプレート型を取るために関数を定義するために、次のようになります。

template <typename Iter> 
void function(Iter first, Iter last){ 
    Unit* val = *first; 
} 

このいくつかの欠点があります。 SFINAEソリューション(boost::enable_ifなど)とは異なり、これは正確にはあなたが求めたものではありません。これは、タイプIterのオブジェクトを参照解除して、Unit*に変換することができる限りコンパイルしますが、これは全く同じではありません。 Iterは完全にSTLに準拠したイテレータであるという保証はありません(operator*を定義する別のタイプかもしれません)。その値のタイプはUnit*です。

一方、はるかに簡単です。

0

さて、私は、後にコンテナを切り替える

template <class T> 
class ContainerIterator 
{ 
    Container(); 
    public: 
    typedef std::list<T>::iterator type; 
} 

//usage: 
void function(ContainerIterator<YourType>::type begin, ContainerIterator<YourType>::type end) 
{ 
    //... 
} 

(私はあなたのユースケースを理解している場合、正しくこのような場合は)一度に一つだけのコンテナ型を必要とする場合には、単純なのtypedefを使用しますtypedef内のコンテナのタイプを変更するだけです。

関連する問題