После того, как вопрос изменился:
Вы используете один и тот же тип, Container
, для ввода и вывода. Но ваш тип ввода и вывода различен : ваш ввод vector<string>
, тогда как ваш вывод vector<int>
. Неудивительно, что C ++ отказывается компилировать это.
Теперь ваша проблема - определить тип возвращаемого значения из типов ввода. Как правило, C ++ не может сделать это. Это так просто: разрешение перегрузки и разрешение шаблона происходит только на основе входных аргументов, а не возвращаемого типа (в некоторых случаях для решения этой проблемы могут использоваться сложные приемы, включающие прокси-объекты и неявные преобразования типов, но давайте не будем останавливаться на этом).
Самое простое и идиоматическое решение - просто указать тип возвращаемого элемента вручную при вызове функции, например:
foomatic = mymap<int>(bar, [] (string s)->int {return atoi(s.c_str());});
Для этого необходимо, чтобы тип возвращаемого элемента был первым в списке аргументов шаблона:
template <
typename ResultType,
template<typename> class Container,
typename InputType,
typename UnaryOp>
Container<ResultType> mymap(Container<InputType> c, UnaryOp op) { ... }
Однако , который не работает , потому что std::vector
не соответствует объявлению template<typename> class
. Зачем? Простая причина: потому что он имеет более одного аргумента шаблона. В частности, стандарт гласит, что у него есть как минимум один дополнительный аргумент шаблона для указания распределителя.
Решение: объявить аргумент шаблона как template<typename, typename> class
, верно?
Нет. Теперь этот работает для некоторых стандартных реализаций библиотеки. Но помимо обязательных двух аргументов шаблона контейнеры могут иметь дополнительные аргументы шаблона, которые принимают значения по умолчанию (это часто используется для передачи классов политики в контейнер, например; распределитель уже является таким классом политики).
Это фундаментальная проблема: мы не можем объявить Container
, чтобы она соответствовала всем возможным сигнатурам типов контейнеров в C ++. Так что это решение тоже не разрешено.
Лучшее решение, к сожалению, более сложное, нам нужно явно привязать тип контейнера. Это мы можем сделать с помощью дополнительной метафункции:
template <typename C, typename T>
struct rebind;
Нам нужно частично специализировать эту метафункцию для каждого возможного числа параметров шаблона. Например, чтобы он работал с минимальным значением std::vector
, нам потребуется следующая частичная специализация:
template <
template <typename, typename> class C,
typename Old,
typename New,
typename A>
struct rebind<C<Old, A>, New> {
typedef typename A::template rebind<New> Rebound;
typedef C<New, typename Rebound::other> type;
};
Это выглядит устрашающе. Для этого нужно взять существующий std::vector<foo>
и тип bar
и переписать его в std::vector<bar>
. Сложность в том, что нам также нужно переписать тип allocator . Это делается с помощью довольно сложной Rebound
декларации.
Теперь мы можем написать вашу функцию и вызвать ее:
template <
typename ResultType,
typename C,
typename UnaryOp>
typename rebind<C, ResultType>::type
mymap(C const& c, UnaryOp op)
{
typename rebind<C, ResultType>::type result;
for(typename C::const_iterator i = c.begin();
i != c.end();
i++)
{
result.push_back(op(*i));
}
return result;
}
int main() {
vector<string> bar;
bar.push_back("1");
bar.push_back("2");
bar.push_back("3");
vector<int> foomatic =
mymap<int>(bar, [] (string s)->int {return atoi(s.c_str());});
}
Кусок торта. Действительно очень сложный торт.
Ответ на старый вопрос:
Если у вас есть параметр шаблона, который сам является шаблоном класса, вам нужно объявить его следующим образом:
template <
template<typename> class Container,
typename ResultType,
typename UnaryOp>
Container<ResultType> mymap(Container<ResultType> c, UnaryOp op) { ... }
template<typename> class Container
имитирует синтаксис объявления шаблона класса и сообщает компилятору, что «Container
- это шаблон класса, который ожидает один аргумент шаблона».
Но библиотеки обычно избегают этих вложенных шаблонных объявлений и вместо этого используют черты / метафункции для передачи такой информации. То есть, как правило, это будет записано следующим образом:
template <typename Container, typename UnaryOp>
Container mymap(Container c, UnaryOp op) {
typedef typename Container::value_type ResultType;
}
(typename
в typedef необходимо, потому что имя является зависимым именем и C ++ не может понять, что это имя типа.)
Этот пример имитирует стандартную библиотечную конвенцию о наличии typedef value_type
внутри каждого контейнера для связанного с ним типа значения. Другие библиотеки могут следовать другим схемам. Например, я помогаю библиотеке, которая использует внешние метафункции, которые работают следующим образом:
template <typename Container, typename UnaryOp>
Container mymap(Container c, UnaryOp op) {
typedef typename Value<Container>::Type ResultType;
}
Идея та же, единственное отличие состоит в том, что Container::value_type
был «отдан на аутсорсинг» независимому типу.