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->x
as(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)->x
would 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