There's no need to explicitly specify the result of map, it can be deduced. I'm also going to accept any range (something that provides begin and end), simply because doing so is trivial. I could make it even more generic and use the free begin and end versions, but that makes it even more complicated, so I won't.
template <typename Range, typename Func>
auto map(const Range& r, Func f)
-> std::vector<typename std::decay<decltype(f(*r.begin()))>::type> {
std::vector<typename std::decay<decltype(f(*r.begin()))>::type> result;
for (const auto& e : r) {
result.push_back(f(e));
}
// Alternatively:
//std::transform(r.begin(), r.end(), std::back_inserter(result), f);
return result;
}
This isn't exactly trivial code, so let me explain.
First, I use the late-specified return type syntax here: I start the function with auto and put the actual return type after the parameters, indicated with ->. This allows me to use parameter names in the return type specification, which is very useful in the decltype stuff I'm doing next.
So what do we actually want? We want a vector of whatever f returns when called with elements of r. What is that? Well, we can use decltype to find out. decltype(expr) gives you the type of expr. In this case, the expression is a call to f: decltype(f(arguments)). We have one argument: an element of the range. The only things a range gives us are begin() and end(), so let's use that: dereference begin() to get the actual value. The expression is now decltype(f(*r.begin())). Note that this is never actually evaluated, so it doesn't matter if the range is empty.
Ok, this gives us the return type of the function. But if we write std::vector<decltype(...)> that leaves us with a problem: the return type of the function could be a reference, but a vector of references is not valid. So we apply the std::decay metafunction to the return type, which removes references and cv-qualifiers on the referenced type, so if the function returns const Foo&, the result of std::decay is just Foo.
This leaves me with the final return type std::vector<typename std::decay<decltype(f(*r.begin()))>::type>.
And then you get to repeat the same thing to declare the actual variable holding the return value. Unfortunately, because there's no way to put type aliases at any reasonable point, you can't get rid of this.
Anyway, there was another problem with your original code, and that was declaring the loop variable as auto. If you call this map with a vector<Bar>, you end up with the loop
for (Bar b : in) { ... }
Notice how your loop variable is a value? This means that you copy every element of in to a local variable. If a Bar is expensive to copy, this is a serious performance issue. And if your transformation relies on the object identity of its argument (e.g. you return a pointer to a member), then your performance issue has become a correctness issue, because the resulting vector is full of dangling pointers. This is why you should use const auto& in the loop, or just use the std::transform algorithm internally, which gets this right.