Принять шаблонный параметр типа stl_container_000 * :: iterator - PullRequest
7 голосов
/ 27 мая 2010

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

Ответы [ 4 ]

4 голосов
/ 27 мая 2010

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

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

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

2 голосов
/ 27 мая 2010

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

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

1 голос
/ 27 мая 2010

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

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

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

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

0 голосов
/ 27 мая 2010

Попробуйте

#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
}
...