In this ad:
template <typename Function, typename Input, typename Output> std::vector<Output> map(const Function &f, const std::vector<Input> &xs);
Output is a non-output context. The compiler infers the types Function and Input from the supplied arguments, but Output cannot be inferred - it must be explicitly provided. This will not happen.
What you want to do is figure out yourself that Output is a Function and Input function. With a C ++ 17 compiler / library, std::invoke_result_t (in C ++ 11 use result_of ). I.e:
template <typename Function, typename Input, typename Output = std::invoke_result_t<Function const&, Input const&>> std::vector<Output> map(const Function &f, const std::vector<Input> &xs);
This is the default template parameter, but since the user will never actually provide it, the default argument you want is used. Now this is not entirely correct, since invoke_result_t can return what you cannot put in vector (for example, a link). Therefore, we need std::decay it. In addition, you want to reserve the output vector, since we know what its size is:
template <typename Function, typename Input, typename Output = std::decay_t<std::invoke_result_t<Function&, Input const&>>> std::vector<Output> map(Function&& f, const std::vector<Input> &xs) { std::vector<Output> res; res.reserve(xs.size()); for (auto&& elem : xs) { res.push_back(f(elem)); } return res; }
Now, if you want it to be able to take a string and either return a vector<X> or a string , which now becomes quite complicated. You cannot overload the return type in C ++, therefore, provided that these two overloads are poorly formed. This is happening in your case now, since overloading string --> vector<X> will be removed from consideration due to X not being output. But as soon as you fix it, you will encounter this problem.