Как специализировать итератор по типу его значения в C ?

#c #templates #iterator #template-specialization

#c #шаблоны #итератор #шаблон-специализация

Вопрос:

Возможно ли специализировать параметр шаблона итератора по его value_type ?

У меня есть функция со следующим прототипом.

 template<typename InputIterator>
void f(InputIterator first, InputIterator last);
  

И я хочу обработать специально, если InputIterator::value_type является SomeSpecificType.

Комментарии:

1. Я не уверен, чего вы хотите. Предназначено ли это для использования со специализацией шаблона? Есть ли причина, по которой вы не можете просто использовать приведенное выше и ссылаться на InputIterator::value_type в теле?

2. Хм, вы могли бы выполнять все виды операций с описанием типов, но если вы уже хотите _input_iterator с определенным типом значения, не будет ли это означать, что все, что вы получаете, является производным классом с базовым классом std::iterator<input_iterator_tag, YourCrazyType> ? Таким образом, вам вообще не понадобятся никакие шаблоны, только одна фиксированная функция.

3. @Kerrek: нет, это не значит, что. Итераторы не используют общий базовый класс. Показательный пример: указатели являются итераторами.

4. @Kerrek: std::iterator<...> не является полиморфным и не реализует никаких полезных операций.

5. Вы правы, извините за это. Еще одна вещь, которую мы узнали об итераторах 🙂 Приведенный ниже ответ Люка прекрасно объясняет это.

Ответ №1:

Вы можете использовать некоторые промежуточные структуры, чтобы получить необходимую вам частичную специализацию шаблона. Что-то вроде этого должно сработать

 template<typename T, typename V>
struct f_impl
{
  static void f( T first, T last ) {...}; //Default version
};

template<typename T>
struct f_impl<T, SomeSpecificType>
{
   static void f(T first,T last) {...}; //Specialisation
};

template<typename InputIterator> void f(InputIterator first, InputIterator last)
{
  f_impl<
      InputIterator,
      typename std::iterator_traits<InputIterator>::value_type
  >::f(first,last);
};
  

Комментарии:

1. typename std::iterator_traits<InputIterator>::value_type было бы лучше, чем typename InputIterator::value_type .

Ответ №2:

Используя SFINAE, предполагая, что enable_if[_c] и is_same либо из Boost, либо <type_traits> (и соответствующим образом квалифицируются с помощью boost:: or std:: соответственно):

 template<typename InputIterator>
typename enable_if<
    !is_same<
        typename std::iterator_traits<InputIterator>::value_type,
        SomeSpecificType
    >::value
>::type
f(InputIterator first, InputIterator last)
{
    // Default implementation.
}

template<typename InputIterator>
typename enable_if<
    is_same<
        typename std::iterator_traits<InputIterator>::value_type,
        SomeSpecificType
    >::value
>::type
f(InputIterator first, InputIterator last)
{
    // Special case
}
  

В случае Boost используйте boost::enable_if_c для чего-то подобного приведенному выше. Вы можете использовать boost::enable_if и избавиться от ::value , но затем также должны использовать, например, boost::disable_if .

Комментарии:

1. Обратите внимание, что boost::enable_if требуется метафункция, тогда как std::enable_if требуется bool значение. Правильное следствие Boost в этом контексте boost::enable_if_c .

2. Ох; это то, что я получаю за чтение кода и только беглый просмотр окружающего текста. 😛

Ответ №3:

Как насчет:

 template<typename T>
typename std::enable_if<std::is_same<typename T::value_type, SomeType>::value, void>::type
f(T first, T second);
  

Комментарии:

1. @Martinho Fernandes: вы также можете ограничить вторую версию f , чтобы устранить двусмысленность

2. @Martino: у вас может быть столько перегрузок, сколько вам нужно, если вы правильно enable_if выполняете каждую из них.

3. @Dani: Я не вижу простого способа добавить это ограничение без вызова функции-оболочки, как в ответе @ Michael Anderson.

4. @Martinho: У меня много перегрузок операторов в AXE, все делается с помощью возможностей SFINAE. Вы не смогли бы сделать это по-другому, когда вам нужно сохранить идеальную пересылку.

Ответ №4:

Это сработает. Этот тип специализации работает только со структурами, поэтому я не могу сделать это с помощью функции.

 template <typename InputIterator, typename ValueType = typename InputIterator::value_type>
struct foobar
{
    static void invoke(InputIterator first, InputIterator second)
    {
        // ...
    }
};

template <typename InputIterator>
struct foobar<InputIterator, SomeSpecificType>
{
    static void invoke(InputIterator first, InputIterator second)
    {
        // ...
    }
};
  

Это не должно требовать от вас установки типа. Это должно быть выведено автоматически.

Ответ №5:

Назовите меня наивным, но почему следующего недостаточно?

 struct MyType; // the only type I want

#include <iterator>
typedef std::iterator<std::input_iterator_tag, MyType> MyIt;

void f(const MyIt amp; begin, const MyIt amp; end)
{
   /* ... */
}
  

Хорошо, забудьте о том, что выше, это была бессмыслица. Вот способ сделать это, который является правильным ответом Luc выше, для C 0x:

 #include <vector>
#include <iterator>
#include <type_traits>

// "int" is our desired iterator value type, "void" is f's return type.
template <typename It>
typename std::enable_if<std::is_same<int, typename std::iterator_traits<It>::value_type>::value, void>::type
f(const It amp; begin, const It amp; end) { /* your function here */ }

int main()
{
  std::vector<double> x;
  std::vector<int> y;

  //f(x.cbegin(), x.cend()); // error
  f(y.cbegin(), y.cend());   // works
}
  

Комментарии:

1. Теперь попытайтесь сделать что-нибудь вообще полезное с begin или end в этом специализированном f .

2. Верно, я понимаю. Наследование от std::iterator предназначено только для целей определения типов… тогда неважно. Реальный ответ, вероятно, все-таки включал бы в себя некоторую работу по описанию типов.