C++ equivalent of C#'s Func<T, TResult>
The following code computes the average of a particular property of T
in the items
collection:
public double Average<T>(IList<T> items, Func<T, double> selector)
{
double average = 0.0;
for (int i = 0; i < items.Count; i++)
{
average += selector(items[i])
}
return average / items.Count;
}
I can then call this with a lambda
expression:
double average = Average(items, p => p.PropertyName);
How would I go about doing this in c++? Here's what I have so far:
template <typename T>
double average(const vector<T>& items, ?)
{
double average= 0.0;
for (int i = 0; i < items.size(); i++)
{
average += ?;
}
return average/ items.size();
}
How might I go about calling this with a c++ lambda
?
Edit: Thank you all very much, here's what I ended up with:
template <typename T>
double average(const vector<T>& items, functio开发者_JS百科n<double(T)> selector)
{
double result = 0.0;
for (auto i = items.begin(); i != items.end(); i++)
{
result += selector(*i);
}
return result / items.size();
}
And in main()
:
zombie z1;
z1.hit_points = 10;
zombie z2;
z2.hit_points = 5;
items.push_back(z1);
items.push_back(z2);
double average = average<zombie>(items, [](const zombie& z) {
return z.hit_points;
});
The equivalent thing would be a std::function<double(T)>
. This is a polymorphic function object which may accept any function object that has the correct operator()
, including lambdas, hand-written functors, bound member functions, and function pointers, and enforces the correct signature.
Do not forget that in C#, the Func
interface (or class, I forget) is written as Func<T, Result>
, whereas C++'s std::function
is written as result(T)
.
Oh, in addition, if you don't have C++0x, there's one in TR1 and also one in Boost, so you should easily be able to access it.
template <typename T, typename Selector>
double average(const vector<T>& items, Selector selector)
{
double average= 0.0;
for (int i = 0; i < items.size(); i++)
{
average += selector(items[i]);
}
return average / items.size();
}
You can use anything callable and copyable as a selector.
template <typename T>
double average(const vector<T>& items, double(*selector)(T))
{
double average= 0.0;
for (int i = 0; i < items.size(); i++)
{
average += selector(items[i]);
}
return average/ items.size();
}
Note that this isn't the best way, it's just the easiest to write and understand. The proper way is to use a functor so it works with C++0x's lambda expressions too.
There are many ways, depending what you want to do, how generic and re-usable the solution should be, what memory constraints you are OK with (i.e. continuously laid out memory vs some linked lists etc). But here is a simple example using C++03:
#include <vector>
#include <list>
#include <iostream>
template <typename Iter, typename Selector>
inline double
average (Iter it, Iter end, Selector selector)
{
double average = 0.0;
size_t size = 0;
while (it != end)
{
average += selector (*it);
++size;
++it;
}
return size != 0 ? average / size : 0.0;
}
struct DoublingSelector
{
template <typename T>
inline T operator () (const T & item)
{
return item * 2;
}
};
int main ()
{
std::vector<int> data;
data.push_back (1);
data.push_back (3);
data.push_back (5);
std::cout << "The average is: " <<
average (data.begin (), data.end (),
DoublingSelector ()) << std::endl;
}
It could be much easier assuming, for example, that you can accept only high level containers (not arrays). And you can get lambdas by throwing in C++0x, for example:
#include <vector>
#include <iterator>
#include <iostream>
template <typename C, typename Selector>
inline double
average (const C & items, Selector selector)
{
auto it = std::begin(items);
auto end = std::end(items);
// Average is only meaningful on a non-empty set of items
assert(it != end);
double sum = 0.0;
size_t size = 0;
for (; it != end; ++it)
{
sum += selector(*it);
++size;
}
return sum / size;
}
int main ()
{
std::vector<int> data = { 1, 3, 5 };
std::cout << "The average is: " <<
average (data, [] (int v) { return v * 2; }) << std::endl;
}
The choice is yours :-)
P.S.: Yeah, and that std::function
(which is actually stuff from Boost in pre-C++0x) can be used to make your algorithm non-template. Otherwise it only limits you in terms of what "selector" you can pass in.
精彩评论