Function template as parameter

I am trying to implement a map function from Python in C ++ 11. It seems to work for any kind of called object, but I have to specify a template type parameter if I want it to work with function templates. Example:

#include <iostream> #include <list> template<typename T> T abs(T x) { return x < 0 ? -x : x; } int main() { std::list<int> li = { -1, -2, -3, -4, -5 }; for (auto i: map(&abs<int>, li)) { std::cout << i << std::endl; } } 

It works fine, but I would like it to infer the int parameter from the second argument of the function, and therefore could write:

 for (auto i: map(&abs, li)) { std::cout << i << std::endl; } 

My map function is written as:

 template<typename Callable, typename Container> auto map(const Callable& function, Container&& iter) -> MapObject<Callable, Container> { return { function, std::forward<Container>(iter) }; } 

where MapObject is part of the implantation and not the real problem. How can I change my definition so that the template type of the Callable object can be inferred from the Container object? For example, how map know that we should use abs<int> for a given abs when a list<int> ?

+4
source share
2 answers

It works fine, but I would like it to infer the int parameter from the second argument of the function, and therefore could write:

 for (auto i: map(&abs, li)) { std::cout << i << std::endl; } 

The problem is that abs not a function, but a function template, and therefore there is no abs address, although &abs<int> exists, since abs<int> (specialization) is really a function (generated from the template).

Now the question is what you really want to solve, and in particular, you should understand that C ++ is a statically typed language, where python is a dynamically typed language. I don’t understand what you are trying to achieve here at different levels. For example, the map function in python has an equivalent in std::transform in C ++:

 a = [ 1, 2, 3 ] a = map(lambda x: 2*x, a) std::vector<int> v{1,2,3}; std::transform(v.begin(),v.end(),v.begin(),[](int x){ return 2*x; }); 

Where I cheated a little, because in python it will create another container, but in C ++ transform works at the iterator level and does not know the container, but you can get the same effect in the same way:

 std::vector<int> v{1,2,3}; std::vector<int> result; // optionally: result.reserve(v.size()); std::transform(v.begin(),v.end(), std::back_inserter(result), [](int x) { return 2*x; }); 

I would advise you to learn idioms in a language, and not try to introduce idioms from other languages ​​...

By the way, if you want the user to specify the type of functor that is passed to the map function, you can simply pass the name of the template and let the compiler find out what specialization you need:

 template <typename Container> auto map(Container && c, typename Container::value_type (*f)(typename Container::value_type)) -> MapObject<Callable<T>,Container>; template <typename T> T abs(T value); int main() { std::vector<int> v{1,2,3,4}; map(v,abs); } 

This is less general than what you tried to do, since it only accepts function pointers and a specific type (this is even less general than std::transform ), and it works like when the compiler sees abs (without & ), he will resolve it to a template and, therefore, to a set of specializations. He will then use the expected type to select one specialization and pass it. In this case, the compiler will implicitly do &abs<int> for you.

Another universal alternative is not using functions, but functors. With this in mind, you can define abs as:

 struct abs { template <typename T> T operator()(T t) { ...} }; 

And then pass a copy of the functor instead of the function pointer. There is no need to specify the overload that will be used when you pass the abs object to the map function only when it is used. The caller’s side will look like this:

 for (auto& element : map(container,abs())) 

If an extra set of brackets creates an object of type abs and passes it.

In general, I will try to avoid this. This is a fun thing, and you can probably come up with a good solution, but it will be difficult and will require quite a bit of C ++ experience. Since the language is not supported by the language, you will have to create something that works in the language, and this requires compromises in various functions or syntax. Knowing options is a difficult problem in itself; understanding compromises is even more complicated and a much more complicated solution. And a good solution is likely to be worse than the equivalent idiomatic C ++ code.

If you are programming in C ++, the C ++ program. Trying to compile python using a C ++ compiler is likely to give you C ++ pain and python performance.

+5
source

This does not output it, because you never indicated that Callable is a template. You make Callable a template template parameter, and it should infer its type for you.

 template<template <typename T> typename Callable, typename Container> auto map(const Callable<T>& function, Container&& iter) -> MapObject<Callable<T>, Container> { return { function, std::forward<Container>(iter) }; } 

You can be bitten, although you cannot take the address of the template to be created. You don’t know why you need an address, though ...

-1
source

All Articles