Функциональный C++ через злоупотребление шаблоном

Я решил попытаться написать реализацию функциональной карты на 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.

Приложение 1

Возможно ли это в 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 отлично, но не цепной.

2 ответа

Решение

Аргументы шаблона никогда не могут быть выведены из вложенных типов. Даже если 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 преобразуется из типа значения в первом контейнере в тип значения во втором.

Другие вопросы по тегам