Преобразование лямбда-функции в std :: tr1 :: function - PullRequest
4 голосов
/ 09 сентября 2010

Использование Visual Studio 2008 с пакетом обновления tr1 и Intel C ++ Compiler 11.1.071 [IA-32], это связано с моим другим вопросом

Я пытаюсь написать функциональную карту для c ++, которая работала бы как версия ruby ​​

strings = [2,4].map { |e| e.to_s }

Итак, я определил следующую функцию в пространстве имен VlcFunctional

template<typename Container, typename U>
vector<U> map(const Container& container, std::tr1::function<U(Container::value_type)> f)
{
    vector<U> transformedValues(container.size());
    int index = -1; 
    BOOST_FOREACH(const auto& element, container)
    {
        transformedValues.at(++index) = f(element);
    }
    return transformedValues; 
}

и вы можете назвать это так (обратите внимание, что аргументы шаблона функции определены явно):

vector<int> test;
test.push_back(2); test.push_back(4); 
vector<string> mappedData2 = VlcFunctional::map<vector<int>,string>(test, [](int i) -> string
{
    return ToString(i);
});

Или примерно так (обратите внимание, что аргументы шаблона функции не определены явно)

std::tr1::function f = [](int i) -> string { return ToString(i); };
vector<string> mappedData2 = VlcFunctional::map<vector<int>,string>(test, f);

Но, главное, НЕ НРАВИТСЯ

vector<string> mappedData2 = VlcFunctional::map(test, [](int i) -> string { return ToString(i); });

Без явного определения аргументов шаблона hte он не знает, какой шаблон использовать, и падает с ошибкой компиляции

 ..\tests\VlcFunctional_test.cpp(106): error: no instance of function template "VlcFunctional::map" matches the argument list, argument types are: (std::vector<int, std::allocator<int>>, __lambda3)

Необходимость определения аргументов шаблона делает его гораздо более громоздким синтаксисом, и я стремлюсь к минимальной затрате времени на сайте вызовов - есть идеи, почему он не знает, как происходит преобразование? Это проблема компилятора или язык не допускает такой тип вывода аргументов шаблона?

1 Ответ

4 голосов
/ 09 сентября 2010

Проблема в том, что лямбда не является std::function, даже если она может быть преобразована.При выводе аргументов типа компилятору не разрешается выполнять преобразования для фактически предоставленных аргументов.Я бы искал способ, чтобы компилятор обнаружил тип U и позволил компилятору освободить второй аргумент:

template <typename Container, typename Functor>
std::vector< XXX > VlcFunctional::map( Container &, Functor )...

Теперь вопрос в том, что написать в XXX.У меня нет того же компилятора, что и у вас, и все возможности C ++ 0x все еще немного хитры.Сначала я попытался бы использовать decltype:

template <typename Container, typename Functor>
auto VlcFunctional::map( Container & c, Functor f ) -> std::vector< decltype(f(*c.begin())) > ...

Или, может быть, набирать черты, если компилятор еще не поддерживает decltype.

Также обратите внимание, что код, который вы пишете, довольнооднотипный в C ++.Обычно при манипулировании контейнерами функции реализуются в терминах итераторов, и вся ваша карта в основном является старой std::transform:

std::vector<int> v = { 1, 2, 3, 4, 5 };
std::vector<std::string> s;
std::transform( v.begin(), v.end(), std::back_inserter(s), [](int x) { return ToString(x); } );

, где std::transform - версия вашей функции map на C ++.Хотя синтаксис является более громоздким, преимущество заключается в том, что вы можете применить его к любому контейнеру и создать выходные данные для любого другого контейнера, поэтому преобразованный контейнер не фиксируется на std::vector.

РЕДАКТИРОВАТЬ: Третий подход, который, вероятно, проще реализовать с вашей текущей поддержкой компилятора, вручную предоставляет только тип возвращаемого значения лямбды в качестве аргумента шаблона и позволяет компилятору выводить остальное:

template <typename LambdaReturn, typename Container, typename Functor>
std::vector<LambdaReturn> map( Container const & c, Functor f )
{
   std::vector<LambdaReturn> ret;
   std::transform( c.begin(), c.end(), std::back_inserter(ret), f );
   return ret;
}
int main() {
   std::vector<int> v{ 1, 2, 3, 4, 5 };
   auto strs = map<std::string>( v, [](int x) {return ToString(x); });
}

Даже еслиВы хотите добавить синтаксический сахар в вашу функцию map, нет необходимости вручную ее реализовывать, когда вы можете использовать существующие функции.

...