обнаружение typedef во время компиляции (шаблонное метапрограммирование) - PullRequest
12 голосов
/ 20 октября 2011

Я сейчас занимаюсь метапрограммированием некоторых шаблонов. В моем случае я могу обрабатывать любой «итеративный» тип, то есть любой тип, для которого typedef foo const_iterator существует таким же образом. Для этого я пытался использовать новое метапрограммирование шаблона C ++ 11, однако не смог найти метод, позволяющий определить, отсутствует ли определенный тип.

Поскольку мне также необходимо включить / выключить другие специализации шаблона, основанные на других характеристиках, в настоящее время я использую шаблон с двумя параметрами, а второй создается с помощью std::enable_if. Вот что я сейчас делаю:

template <typename T, typename Enable = void>
struct Foo{}; // default case is invalid

template <typename T>
struct Foo< T, typename std::enable_if<std::is_fundamental<T>::value>::type>{ 
   void do_stuff(){ ... }
};

template<typename T>
struct exists{
   static const bool value = true;
};

template<typename T>
struct Foo<T, typename std::enable_if<exists< typename T::const_iterator >::value >::type> {
    void do_stuff(){ ... }
};

Мне не удалось сделать что-то подобное без вспомогательного шаблона exists. Например, просто сделать

template<typename T>
struct Foo<T, typename T::const_iterator> {
    void do_stuff(){ ... }
};

не сработало, потому что в тех случаях, когда следует использовать эту специализацию, вместо этого создавался недействительный случай по умолчанию.

Однако я не смог найти этот exists в новом стандарте C ++ 11, который, насколько я знаю, просто берет boost::type_traits для такого рода вещей. Однако на домашней странице для boost::type_traits нет ссылки на что-либо, что можно использовать вместо этого.

Эта функциональность отсутствует, или я упустил какой-то другой очевидный способ достижения желаемого поведения?

Ответы [ 4 ]

14 голосов
/ 20 октября 2011

Если вы просто хотите, чтобы данный тип содержал const_iterator, то ниже приведена упрощенная версия вашего кода:

template<typename T>
struct void_ { typedef void type; };

template<typename T, typename = void>
struct Foo {};

template<typename T>
struct Foo <T, typename void_<typename T::const_iterator>::type> {
      void do_stuff(){ ... }
};

См. этот ответ для объяснения того, как работает эта техника.

7 голосов
/ 20 октября 2011

Вы можете создать черту has_const_iterator, которая предоставляет логическое значение, и использовать ее в специализации.

Что-то подобное может сделать это:

template <typename T>
struct has_const_iterator {
private:
    template <typename T1>
    static typename T1::const_iterator test(int);
    template <typename>
    static void test(...);
public:
    enum { value = !std::is_void<decltype(test<T>(0))>::value };
};

И затем вы можете специализироватьсякак это:

template <typename T,
          bool IsFundamental = std::is_fundamental<T>::value,
          bool HasConstIterator = has_const_iterator<T>::value>
struct Foo; // default case is invalid, so no definition!

template <typename T>
struct Foo< T, true, false>{ 
   void do_stuff(){// bla }
};

template<typename T>
struct Foo<T, false, true> {
    void do_stuff(){//bla}
};
4 голосов
/ 20 октября 2011

Вот еще одна версия проверки черты типа члена:

template<typename T>
struct has_const_iterator
{
private:
    typedef char                      yes;
    typedef struct { char array[2]; } no;

    template<typename C> static yes test(typename C::const_iterator*);
    template<typename C> static no  test(...);
public:
    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
1 голос
/ 02 июля 2014

Есть несколько способов сделать это. В C ++ 03 вы можете использовать boost и enable_if для определения черты ( docs , source ):

BOOST_MPL_HAS_XXX_TRAIT_DEF(const_iterator);

template <typename T, typename Enable = void>
struct Foo;

template <typename T>
struct Foo< T, typename boost::enable_if<boost::is_fundamental<T> >::type>{ 
   void do_stuff(){ ... }
};

template<typename T>
struct Foo<T, typename boost::enable_if<has_const_iterator<T> >::type> {
    void do_stuff(){ ... }
};

В C ++ 11 вы можете использовать Tick следующим образом:

TICK_TRAIT(has_const_iterator)
{
    template<class T>
    auto require(const T&) -> valid<
        has_type<typename T::const_iterator>
    >;
};

template <typename T, typename Enable = void>
struct Foo;

template <typename T>
struct Foo< T, TICK_CLASS_REQUIRES(std::is_fundamental<T>::value)>{ 
   void do_stuff(){ ... }
};

template<typename T>
struct Foo<T, TICK_CLASS_REQUIRES(has_const_iterator<T>())> {
    void do_stuff(){ ... }
};

Также с помощью Tick вы можете еще больше улучшить эту черту, чтобы фактически обнаружить, что const_iterator также является итератором. Допустим, мы определили простую черту is_iterator следующим образом:

TICK_TRAIT(is_iterator,
    std::is_copy_constructible<_>)
{
    template<class I>
    auto require(I&& i) -> valid<
        decltype(*i),
        decltype(++i)
    >;
};

Затем мы можем определить черту has_const_iterator, чтобы проверить, соответствует ли тип const_iterator черте is_iterator следующим образом:

TICK_TRAIT(has_const_iterator)
{
    template<class T>
    auto require(const T&) -> valid<
        has_type<typename T::const_iterator, is_iterator<_>>
    >;
};
...