#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 предназначено только для целей определения типов… тогда неважно. Реальный ответ, вероятно, все-таки включал бы в себя некоторую работу по описанию типов.