У меня есть функция, где у меня есть контейнер, который содержит строки (например, vector<string>
, set<string>
, list<string>
) и, учитывая итератор запуска и конечный итератор, пройдите диапазон итератора, обрабатывающий строки.
В настоящее время функция объявляется как это:
template< typename ContainerIter>
void ProcessStrings(ContainerIter begin, ContainerIter end);
Теперь это примет любой тип, который соответствует неявному интерфейсу реализации operator*
, префикс operator++
и независимо от того, что другие вызовы находятся в теле функции.
То, что я действительно хочу сделать, имеют определение как то, ниже которого явно ограничивает сумму входа (псевдокод, предупреждающий):
template< typename Container<string>::iterator>
void ProcessStrings(Container<string>::iterator begin, Container<string>::iterator end);
так, чтобы я мог использовать его как таковой:
vector<string> str_vec;
list<string> str_list;
set<SomeOtherClass> so_set;
ProcessStrings(str_vec.begin(), str_vec.end()); // OK
ProcessStrings(str_list.begin(), str_list.end()); //OK
ProcessStrings(so_set.begin(), so_set.end()); // Error
По существу то, что я пытаюсь сделать, ограничивают функциональную спецификацию для создания этого очевидным для пользователя функции, что это принимает и если коду не удается скомпилировать, они получают сообщение, что они используют неправильные типы параметра, а не что-то в теле функции, которым XXX функций не могли быть найдены для XXX классов.
Вы можете приблизиться к этому с помощью параметра шаблона шаблона:
template<template<class> class CONTAINER>
void ProcessStrings(CONTAINER<string>&);
Это обработает весь контейнер и выдаст ошибку компиляции, если он не содержит строк.
ProcessStrings(str_vec); // OK
ProcessStrings(so_set); // Error
Если вы хотите работать с диапазонами итераторов, то лучшее, что я мог бы сделать, это
template<template<class> class CONTAINER>
void ProcessStrings(typename CONTAINER<string>::iterator,
typename CONTAINER<string>::iterator);
К сожалению, вывод типа не будет работать с аргументами функции, поэтому вам придется явно указать параметр шаблона:
ProcessStrings<vector>(str_vec.begin(), str_vec.end()); // OK
ProcessStrings<set>(so_set.begin(), so_set.end()); // Error
Кто-нибудь может это улучшить?
Простой, но эффективный способ.
template <class T>
struct is_basic_string: boost::mpl::false_ {};
template <class CharT, class Traits, class Alloc>
struct is_basic_string< std::basic_string<CharT, Traits, Alloc> >:
boost::mpl::true_ {};
А затем используйте его для проверки типа значения
void ProcessStrings(Iterator begin, Iterator end)
{
BOOST_MPL_ASSERT_MSG(
is_basic_string< typename boost::value_type<Iterator>::type >,
ONLY_ACCEPT_ITERATOR_TO_BASIC_STRING,
(Iterator)
);
// do your work
}
Проверьте ссылку здесь, этот макрос предназначен для предоставления как можно более значимых сообщений во время компиляции.
Также это немного более общее решение, чем Sebastian
... но std::wstring
довольно удобен для интернационализации, и вы не захотите подавиться им.
Теперь очень хороший вопрос... зачем это нужно!
Сама цель родового программирования - создать функции, которые могут работать с любым типом, соответствующим операциям, которые они используют внутри. Почему вы хотите намеренно ограничить это?
Try
#include <string>
#include <vector>
#include <list>
template<template<typename T,typename A> class C>
void ProcessStrings(typename C<std::string, std::allocator<std::string> >::iterator begin,
typename C<std::string, std::allocator<std::string> >::iterator end)
{
}
int main()
{
std::vector<std::string> strVec;
std::list<std::string> strList;
std::list<int> intList;
ProcessStrings<std::vector>(strVec.begin(), strVec.end());
ProcessStrings<std::list>(strList.begin(), strList.end());
ProcessStrings<std::list>(intList.begin(), intList.end()); // This will fail
}
Вы можете реализовать такие проверки, используя магию шаблона boost :: enable_if
. Приведенный ниже метод не будет скомпилирован, если тип значения итератора не имеет строкового типа.
template<class It>
boost::enable_if_c<
boost::is_same< typename boost::iterator_value<It>::type, string >::value
>::type
ProcessStrings(It itBegin, It itEnd)
{ }
Если boost :: iterator_value
имеет тип string, boost :: enable_if <...> :: type
будет признан недействительным, ваш возврат параметр. В противном случае, по принципу SFINAE (отказ замещения не является ошибкой), метод не будет скомпилирован без ошибки.