C ++ эквивалент C # Func <T, TResult> - PullRequest
9 голосов
/ 10 июня 2011

Следующий код вычисляет среднее значение определенного свойства T в коллекции items:

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;
}

Затем я могу вызвать это с lambda выражением:

double average = Average(items, p => p.PropertyName);

Как бы я поступил так в c ++? Вот что у меня есть:

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();
}

Как я могу назвать это с помощью c ++ lambda?


Редактировать: Большое спасибо всем, вот что я закончил:

template <typename T>
double average(const vector<T>& items, function<double(T)> selector)
{
    double result = 0.0;

    for (auto i = items.begin(); i != items.end(); i++)
    {
        result += selector(*i);
    }

    return result / items.size();
}

А в 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;
});

Ответы [ 4 ]

13 голосов
/ 10 июня 2011

Эквивалентная вещь будет std::function<double(T)>. Это полиморфный функциональный объект, который может принимать любой функциональный объект с правильным значением operator(), включая лямбда-выражения, рукописные функторы, функции связанного члена и указатели функций, и обеспечивают правильную подпись.

Не забывайте, что в C # интерфейс Func (или класс, я забыл) записывается как Func<T, Result>, тогда как std::function в C ++ записывается как result(T).

О, кроме того, если у вас нет C ++ 0x, есть один в TR1 и один в Boost, так что вы легко сможете получить к нему доступ.

4 голосов
/ 10 июня 2011

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();
}

В качестве селектора можно использовать все, что можно вызвать и скопировать.

3 голосов
/ 10 июня 2011

Есть много способов, в зависимости от того, что вы хотите сделать, насколько универсальным и многократно используемым должно быть решение, с какими ограничениями памяти у вас все в порядке (т. Е. Постоянно распределяемая память против некоторых связанных списков и т. Д.).Но вот простой пример с использованием 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;
}

Возможно, будет гораздо проще предположить, например, что вы можете принимать только контейнеры высокого уровня (не массивы).И вы можете получить лямбды, добавив C ++ 0x, например:

#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;
}

Выбор за вами: -)

PS: Да, и это std::function (чтона самом деле материал из Boost в pre-C ++ 0x) может быть использован для того, чтобы сделать ваш алгоритм не шаблонным.В противном случае он ограничивает вас только в отношении того, какой «селектор» вы можете передать.

3 голосов
/ 10 июня 2011
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();
}

Обратите внимание, что это не лучший способ, его проще всего написать и понять. Правильный способ - использовать функтор, чтобы он работал и с лямбда-выражениями C ++ 0x.

...