2009-07-26 12 views
3

私はテンプレートのC++ジェネリックコンテナクラスを作成しています。これは、オプションでその内容を明確な順序で維持することができます。これまでは、関数ポインタを使用してその内容を適切な型固有の方法で並べ替えましたが、代わりにテンプレート関数の引数を使用するように変更しようとしています。C++テンプレートコンテナクラス:順序付けられたアイテムタイプと順序付けされていないアイテムタイプの両方を最適にサポートする方法

クラスのユーザは、同じ型の項目を異なるコンテナで別々の方法で並べ替えることが必要な場合が多いので、コンテナクラスはオプションのテンプレート引数を取ります。

template <class ItemType, class CompareFunctorType = CompareFunctor<ItemType> > class MyContainer 
{ 
    [...] 
}; 

クラスユーザは、デフォルトで次のCompareFunctor定義を使用して、カスタム数子のタイプを指定しない場合:

template <typename ItemType> class CompareFunctor 
{ 
public: 
    bool IsItemLessThan(const ItemType & a, const ItemType & b) const 
    { 
     return (a<b); // will compile only for types with < operator 
    } 
}; 

これは、ビルトインタイプのための素晴らしい作品と、ユーザー定義型wここではより小さい演算子が定義されています。しかし、組み込み関数がないか、またはより小さい演算子が明示的に定義されている型に対しても、自動的に動作するようにしたいと思います。これらのタイプの場合、コンテナ内のアイテムの順序付けは重要ではありません。

私はこのコンテナを使用してさまざまな種類のものを保持していますが、ほとんどの場合、コンテナ内の型の順序は気にしませんが、場合によっては...私は入って、 "ダミー"以下の演算子をこれらの異なる型すべてに追加するだけでよいので、私はこのコンテナクラスでそれらを使用できるようにする必要はありません。明示的に指定する必要はありません小数点以下の演算子を持たない項目を格納するためにテーブルを使うたびに、カスタムの "ダミー" CompareFunctor引数。

テンプレートの特殊化(または何か)を使用して可能な限りデフォルトのCompareFunctor(上の図)が使用されるようにする方法がありますが、そのCompareFunctorがエラーを引き起こす場合、C++は自動的に以下のような "ダミーの" FallbackCompareFunctor?あるいは、このジレンマを扱うための他の巧妙な方法でしょうか?

template <typename ItemType> class FallbackCompareFunctor 
{ 
public: 
    bool IsItemLessThan(const ItemType & a, const ItemType & b) const 
    { 
     return ((&a)<(&b)); // will compile for all types (useful for types where the ordering is not important) 
    } 
}; 

答えて

1

誰かが興味がある場合は、上記の手法を組み合わせて、私が望むことをする方法を考え出すことができました。私の概念実証コード(単体テストあり)を以下に示します。

#include <stdio.h> 

// Real functor, should be used by default when ItemType has a < operator 
template <typename ItemType> class RealCompareFunctor 
{ 
public: 
    bool IsLessThan(const ItemType & item1, const ItemType & item2) 
    { 
     printf(" --> RealCompareFunctor called!\n"); 
     return item1 < item2; 
    } 

    typedef ItemType TheItemType; 
}; 

// Dummy functor, should be used by default when ItemType has no < operator 
template <typename ItemType> class DummyCompareFunctor 
{ 
public: 
    bool IsLessThan(const ItemType & item1, const ItemType & item2) 
    { 
     printf(" --> DummyCompareFunctor called!\n"); 
     return (&item1) < (&item2); 
    } 
}; 

namespace implementation_details 
{ 
    // A tag type returned by operator < for the any struct in this namespace when T does not support (operator <) 
    struct tag {}; 

    // This type soaks up any implicit conversions and makes the following (operator <) 
    // less preferred than any other such operator found via ADL. 
    struct any 
    { 
     // Conversion constructor for any type. 
     template <class T> any(T const&); 
    }; 

    // Fallback (operator <) for types T that don't support (operator <) 
    tag operator < (any const&, any const&); 

    // Two overloads to distinguish whether T supports a certain operator expression. 
    // The first overload returns a reference to a two-element character array and is chosen if 
    // T does not support the expression, such as < whereas the second overload returns a char 
    // directly and is chosen if T supports the expression. So using sizeof(check(<expression>)) 
    // returns 2 for the first overload and 1 for the second overload. 
    typedef char yes; 
    typedef char (&no)[2]; 

    no check(tag); 

    template <class T> yes check(T const&); 

    // Implementation for our has_less_than_operator template metafunction. 
    template <class T> struct has_less_than_operator_impl 
    { 
     static const T & x; 
     static const bool value = sizeof(check(x < x)) == sizeof(yes); 
    }; 

    template <class T> struct has_less_than_operator : implementation_details::has_less_than_operator_impl<T> {}; 

    template <bool Condition, typename TrueResult, typename FalseResult> 
    class if_; 

    template <typename TrueResult, typename FalseResult> 
    struct if_<true, TrueResult, FalseResult> 
    { 
    typedef TrueResult result; 
    }; 

    template <typename TrueResult, typename FalseResult> 
    struct if_<false, TrueResult, FalseResult> 
    { 
     typedef FalseResult result; 
    }; 
} 

template<typename ItemType> struct AutoChooseFunctorStruct 
{ 
    typedef struct implementation_details::if_<implementation_details::has_less_than_operator<ItemType>::value, RealCompareFunctor<ItemType>, DummyCompareFunctor<ItemType> >::result Type; 
}; 

/** The default FunctorType to use with this class is chosen based on whether or not ItemType has a less-than operator */ 
template <class ItemType, class FunctorType = struct AutoChooseFunctorStruct<ItemType>::Type > class Container 
{ 
public: 
    Container() 
    { 
     ItemType itemA; 
     ItemType itemB; 
     FunctorType functor; 
     bool isLess = functor.IsLessThan(itemA, itemB); 
     //printf(" --> functor says isLess=%i\n", isLess); 
    } 
}; 

// UNIT TEST CODE BELOW 

struct NonComparableStruct {}; 

struct ComparableStructOne 
{ 
    bool operator < (ComparableStructOne const&) const { return true; } 
}; 

struct ComparableStructTwo {}; 
bool operator < (ComparableStructTwo const&, ComparableStructTwo const&) { return true; } 

class NonComparableClass 
{ 
public: 
    NonComparableClass() {/* empty */} 
}; 

class ComparableClass 
{ 
public: 
    ComparableClass() {/* empty */} 

    bool operator < (const ComparableClass & rhs) const {return (this < &rhs);} 
}; 

int main(int argc, char * argv[]) 
{ 
    printf("\nContainer<int>\n"); 
    Container<int> c1; 

    printf("\nContainer<ComparableStructOne>\n"); 
    Container<ComparableStructOne> c2; 

    printf("\nContainer<ComparableStructTwo>\n"); 
    Container<ComparableStructTwo> c3; 

    printf("\nContainer<NonComparableStruct>\n"); 
    Container<NonComparableStruct> c4; 

    printf("\nContainer<NonComparableClass>\n"); 
    Container<NonComparableClass> c5; 

    printf("\nContainer<ComparableClass>\n"); 
    Container<ComparableClass> c6; 

    return 0; 
} 
1

デフォルトのソートされていない場合は、すべてのケースでfalseを返すNull比較ファンクタを使用します。
次に、std :: less()ファンクタを使用して、テンプレートをソートされたコンテナに特化できます。

 template<class T> 
     struct NullCompare: public binary_function <T, T, bool> 
     { 
      bool operator()(const T &l, const T &r) const 
      { 
       // edit: previously had "return true;" which is wrong. 
       return false; 
      } 
     }; 

     template <class T, class Compare=NullCompare<T> > 
     class MyContainer 
     { 
      [...] 
     }; 

     template <class T, class Compare=std::less<T> > 
     class MySortedContainer : public MyContainer<T, Compare> 
     { 
      [...] 
     }; 
+1

私はそれをしません。比較機能の契約では、ソートされる項目の部分的な順序が定義されています。 * a !(b b = a *のようなプロパティを満たさなければなりません。いくつかのソートアルゴリズムは、 '' return true' "のような矛盾した比較関数が与えられた場合、実際にクラッシュする可能性があります。 –

+0

はい、私のFallbackCompareFunctor exmapleオブジェクトにアイテムのポインタを比較させた理由はJohnさんの懸念です。おそらく、NullCompareは上記の同じことを行うことができますか? –

+0

おっと、返されているはずです。すなわち、すべてが等しい。 –

0

boost::enable_ifは、コンパイル時の評価に基づいてテンプレートの特殊化のオン/オフを切り替えることができます。

コンパイル時にfalseと評価されるコンストラクタを作成することができますが、チェックしている型にレストシャン演算子がない場合は、それを使用してCompareFunctor :: IsItemLessThanのフォールバック特殊化を有効にできます。もちろん

template <typename ItemType> class CompareFunctor 
{ 
public: 
    bool IsItemLessThan(const ItemType & a, const ItemType & b) const 
    { 
     return OptionalLessThan<ItemType>(a, b); 
    } 
}; 

template<class T> 
typename boost::enable_if<some_condition<T>, bool>::type 
OptionalLessThan(const T& a, const T& b) 
{ 
    return ((&a)<(&b)); 
} 

template<class T> 
typename boost::disable_if<some_condition<T>, bool>::type 
OptionalLessThan(const T& a, const T& b) 
{ 
    return a < b; 
} 

あなたも、私は推測するブースト:: type_traitsとMPLのコードを見てください...何とかlessthanオペレータをチェックするsome_conditionを必要とする - 彼らは同様のものを行います。