The C++11 Standard Library contains a metafunction called result_of. This metafunction computes the return type of a function object. Probably due to its history in boost (and C++03), it is used in a rather peculiar way: You pass it the type of the function object and the type of the arguments you want to call the function object with via a combined function type. For example:
struct my_function_object
{
bool operator()(int);
char operator()(double);
};
std::result_of<my_function_object(int)>::type // yields bool
std::result_of<my_function_object(short)>::type // yields bool
std::result_of<my_function_object(double)>::type // yields char
result_of performs overload resolution. If you call short s{}; my_function_object{}(s);, overload resolution will select my_function_object::operator()(int). Therefore, the corresponding result_of<my_function_object(short)>::type yields bool.
Using this trait, you can simplify the computation of the return type as follows:
template<typename Func, typename RT = std::unordered_map<int,
typename std::result_of<Func(T&)>::type > >
RT mapResult(Func func)
{
RT r;
for (auto &i : mData)
r.insert({i.first, func(i.second)});
return r;
}
The T& parameter tells result_of to use an lvalue argument in overload resolution. The default (for a non-reference type T) is xvalue (T&&).
There is one slight difference to the OP's version: SFINAE will probably not work correctly using std::result_of (in C++11). This was resolved in C++14. See N3462.
C++14 has introduced standardized alias templates like result_of_t so you can get rid of the typename and ::type:
template<typename Func, typename RT = std::unordered_map<int,
std::result_of_t<Func(T&)> > >
RT mapResult(Func func)
{
RT r;
for (auto &i : mData)
r.insert({i.first, func(i.second)});
return r;
}
If you're using Visual Studio 2013 or newer, you can write alias templates yourself. You could also go one step further and write the whole return type as a metafunction:
template<typename FT> using result_of_t = typename std::result_of<FT>::type;
template<typename Func> using RetType =
std::unordered_map<int, result_of_t<Func(T&)> >;
template<typename Func, typename RT = RetType<Func> >
RT mapResult(Func func)
{
RT r;
for (auto &i : mData)
r.insert({i.first, func(i.second)});
return r;
}
Of course, if you have sufficient C++14 core language support (not in VS12), you can use return type deduction as well:
template<typename Func>
auto mapResult(Func func)
{
auto r = std::unordered_map<int, result_of_t<Func(T&)>>{};
for (auto &i : mData)
r.insert({i.first, func(i.second)});
return r;
}
It is also possible to shorten a version using decltype:
using std::declval;
decltype(declval<Func>(T{}))
although this isn't quite correct, both the function object and the argument will be an lvalue:
decltype(declval<Func&>(declval<T&>{}))
declval will use an xvalue in overload resolution for a non-reference type X. By adding the &, we tell it to use an lvalue instead. (result_of is based on declval, so both show this behaviour.)
Note that in any case, it might be useful to run the result_of_t<Func(T&)> type through the std::decay metafunction, to get rid e.g. of references which appear in cases such as:
[](string const& s) -> string const& { return s; } // identity
This does depend on your use case though, and either choice should be documented.
IIRC, emplace is slightly more efficient (in theory) in this situation (inserting unique elements):
r.emplace(i.first, func(i.second));
It might be possible to further optimize this function, e.g. by reserving a bucket count before the insertion, or maybe with an iterator adapter to leverage the constructor for insertion. Using std::transform should also be possible, though I'd guess it cannot be as efficient due to additional moves of the value_type pair.
((Func*)nullptr)needs a set of()because you're using the (C-style) cast-notation. The grammar simply interprets(T)e->xas(T)(e->x). You could use a function-style cast instead,T(e)->x, but this doesn't work withFunc*since it's not a simple-type-specifier. Other casts such asstatic_cast<T>(e)->xwould work. The last parentheses are the function-call-parens ofoperator(), plus the parens for the "cast" (creation of a temporary) of typeT. It could be written asoperator()( T{} ), or just(*static_cast<Func*>(nullptr))( T{} ). - dyp