C ++ 템플릿 컨테이너 클래스 : 순서대로 주문한 항목 유형과 가장 잘 지원하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/1183448

문제

나는 템플릿으로 정의 된 순서로 내용을 선택적으로 유지할 수있는 템플릿 C ++ 일반 컨테이너 클래스를 작성하고 있습니다. 이전에는 함수 포인터를 사용하여 내용을 현명한 유형 별 방식으로 주문했지만 대신 템플릿 기능을 사용하도록 변경하려고합니다.

클래스의 사용자가 동일한 유형의 항목을 다른 컨테이너로 다른 방식으로 정렬하기를 원할 경우 종종 컨테이너 클래스는 사용자가 자신의 비교 분자를 선택적으로 지정할 수있는 선택적 템플릿 인수를 취합니다.

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

클래스 사용자가 사용자 정의 functor 유형을 지정하지 않으면 기본적으로 다음 compresefunctor 정의를 사용합니다.

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
    }
};

이는 내장 유형 및 운영자가 덜 정의 된 사용자 정의 유형에도 적합합니다. 그러나 운영자보다 내장되거나 명시 적으로 정의되지 않은 유형에 대해 자동으로 작동하고 싶습니다. 이러한 유형의 경우 컨테이너 내 항목 주문이 중요하지 않습니다.

동기는이 컨테이너를 사용하여 많은 다른 유형을 고정하고 대부분의 경우 컨테이너의 유형의 순서에 신경 쓰지 않지만 어떤 경우에는 ... 그리고 나는하지 않습니다. 이 컨테이너 클래스와 함께 사용할 수 있도록 이러한 모든 유형에 "더미"가 적은 연산자를 추가해야합니다. 그리고 명시 적으로 사용자 정의 "더미를 지정하고 싶지 않습니다" "테이블을 사용하여 운영자보다 적은 품목을 저장하기 위해 테이블을 사용할 때마다 ComparentFunctor 인수.

따라서 템플릿 전문화 (또는 무언가)를 사용할 수있는 방법이 있으므로 기본 비교 혐오기 (위에 표시)가 가능할 때마다 사용되지만 비교 혐의가 오류를 일으키는 경우 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)
    }
};
도움이 되었습니까?

해결책 3

누구나 관심이있는 경우, 위에서 설명한 기술의 조합을 사용하여 내가 원하는 것을 수행 할 수있는 방법을 생각해 낼 수있었습니다. 내 개념 증명 코드 (단위 테스트 포함)는 다음과 같습니다.

#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;
}

다른 팁

기본 불균형 케이스의 경우 모든 경우에 False를 반환하는 Null Comparison Functor를 사용하십시오.
그런 다음 std :: Less () functor를 사용하여 템플릿을 정렬 된 컨테이너에 전문화 할 수 있습니다.

       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>
       {
           [...]
       };

유진의 답변을 기반으로 Google 검색을 수행하는 동안이 기사를 찾았습니다.

http://www.martinecker.com/wiki/index.php?title=detecting_the_existence_of_operators_at_compile-time

아마도 나는 거기에 제시된 코드를 조정할 수있을 것입니다 ...

부스트 :: enable_if 일부 컴파일 시간 평가를 기반으로 템플릿 전문화를 켜고 끕니다.

확인중인 유형이 연산자보다 적은 경우 Compile Time에 False를 평가할 수있는 구조를 만들 수 있다면 CompartFunctor :: 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; 
}

물론 당신은 또한 어떻게 든 연산자를 확인하기 위해 약간의 _condition이 필요합니다 ... boost :: type_traits 및 mpl 코드를보십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top