Примите шаблонный параметр stl_container_type <строка>:: итератор

У меня есть функция, где у меня есть контейнер, который содержит строки (например, 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 классов.

7
задан Christopher Howlin 27 May 2010 в 15:10
поделиться

4 ответа

Вы можете приблизиться к этому с помощью параметра шаблона шаблона:

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

Кто-нибудь может это улучшить?

4
ответ дан 7 December 2019 в 07:40
поделиться

Простой, но эффективный способ.

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 довольно удобен для интернационализации, и вы не захотите подавиться им.

Теперь очень хороший вопрос... зачем это нужно!

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

1
ответ дан 7 December 2019 в 07:40
поделиться

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
}
0
ответ дан 7 December 2019 в 07:40
поделиться

Вы можете реализовать такие проверки, используя магию шаблона 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 :: type имеет тип string, boost :: enable_if <...> :: type будет признан недействительным, ваш возврат параметр. В противном случае, по принципу SFINAE (отказ замещения не является ошибкой), метод не будет скомпилирован без ошибки.

2
ответ дан 7 December 2019 в 07:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: