Я решил попытаться написать реализацию функциональной карты на C ++ с использованием шаблонов, и вот что я придумал:
template <
class U,
class V,
template <class> class T
>
class T<V> WugMap(
class T<U>::const_iterator first,
class T<U>::const_iterator second,
V (U::*method)() const)
{
class T<V> collection;
while (first != second)
{
collection.insert(collection.end(), ((*(first++)).*method)());
}
return collection;
}
Теперь это все прекрасно и денди, и даже компилируется. Проблема в том, что я понятия не имею, как на самом деле это назвать.
Попытка наивного способа приводит к следующей ошибке:
prog.cpp:42: error: no matching function for call to
‘WugMap(__gnu_cxx::__normal_iterator<Container*, std::vector<Container,
std::allocator<Container> > >, __gnu_cxx::__normal_iterator<Container*,
std::vector<Container, std::allocator<Container> > >, int (Container::*)()const)’
Насколько я могу судить, все аргументы верны. gcc вообще не предлагает никаких альтернатив, что заставляет меня полагать, что мое определение WugMap является подозрительным, но оно компилируется нормально, так что я скорее заблудился. Кто-нибудь может направить меня через эту глупость?
Если кто-нибудь может предложить лучший способ написания такой функции, которая будет поддерживать использование любого типа коллекции, содержащей любой тип объекта, я постараюсь изменить его.
В настоящее время я использую Ideone, который использует C ++ 03, GCC 4.3.4.
Возможно ли это в C ++ 11? Намекнули, что это так. Я знаю, что шаблоны в C ++ 11 поддерживают различное количество аргументов, поэтому я также изменю свои требования в соответствии с этим. Я приложу немного усилий, чтобы что-то написать, но пока вот требования, которые я ищу:
Должна иметь подпись что-то вроде следующего:
C2<V, ...> map(const C1<U, ...>&, V (U::*)(...), ...)
Это берет некоторую коллекцию C1, содержащую элементы типа U, сконструированные с некоторым количеством параметров по умолчанию, по ссылке, а также берет некоторую функцию-член (возвращающую V и принимающую некоторое количество аргументов неизвестных типов) из U, и затем принимающую, в порядок, аргументы для передачи функции-члену. Функция, наконец, вернет коллекцию типа C2, содержащую элементы типа V и инициализируемую с неизвестным количеством параметров по умолчанию.
Должен быть цепным:
vector<int> herp = map(
map(
set<Class1, myComparator>(),
&Class1::getClass2, 2, 3),
&Class2::getFoo);
Бонусные баллы, если у меня нет необходимости использовать аргументы шаблона или другие подробности при его использовании.
std::transform
отлично, но не цепной.
Аргументы шаблона никогда не могут быть выведены из вложенных типов. Даже если U
а также V
может быть выведен из указателя функции-члена, вы не сможете определить тип шаблона T
,
Явное указание аргументов шаблона, как в ссылке на ideone (Я не делал ссылку до написания приведенного выше утверждения) тоже не работает, главным образом потому, что аргументы шаблона для std::vector
не просто один тип T
! std::vector
принимает тип значения и тип распределителя. Исправление становится довольно уродливым:
#include <vector>
#include <iostream>
using namespace std;
class Container
{
public:
Container() {}
Container(int _i) : i(_i) {}
int get_i() const {return i;}
int i;
};
template <
class U,
class V,
template <typename...> class T
>
T<V> WugMap(
typename T<U>::const_iterator first,
typename T<U>::const_iterator second,
V (U::*method)() const)
{
T<V> collection;
while (first != second)
{
collection.insert(collection.end(), ((*(first++)).*method)());
}
return collection;
}
int main()
{
vector<Container> containers;
for (int i = 0; i < 10; ++i) containers.push_back((Container(i)));
WugMap<Container, int, std::vector>(
containers.begin(), containers.end(), &Container::get_i);
}
Не совсем уверен, должен ли это быть ответ, но черт возьми:
std::vector<std::string> src = f();
std::vector<std::string::size_type> sizes;
sizes.reserve(src.size());
// Actual transformation:
std::transform( src.begin(), src.end(), std::back_inserter(sizes),
[](std::string const& s) { return s.size(); } );
Подобные вещи можно сделать вручную, но в действительности нет смысла изобретать заново изобретенное колесо.
Что касается того, что отличается в std::transform
случай, он не пытается связать типы так тесно, это требует Iter1
для первых двух аргументов, Iter2
для третьего аргумента и Functor
для третьего. На интерфейсе нет никаких проверок, чтобы гарантировать, что Iter1
а также Iter2
итераторы в один и тот же тип контейнера, или что Functor
преобразуется из типа значения в первом контейнере в тип значения во втором.