9 Stimmen

C++-Äquivalent zu C#s Func<T, TResult>

Der folgende Code berechnet den Durchschnitt einer bestimmten Eigenschaft von T im items Sammlung:

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

Ich kann dies dann mit einer lambda Ausdruck:

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

Wie würde ich dies in C++ tun? Hier ist, was ich bis jetzt habe:

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

Wie könnte ich dies mit einem C++-Programm aufrufen? lambda ?


Edit: Vielen Dank an alle, hier ist das, was ich am Ende bekommen habe:

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

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

14voto

Puppy Punkte 141483

Das Äquivalent wäre eine std::function<double(T)> . Dies ist ein polymorphes Funktionsobjekt, das jedes Funktionsobjekt akzeptieren kann, das die richtige operator() einschließlich Lambdas, handgeschriebenen Funktoren, gebundenen Mitgliedsfunktionen und Funktionszeigern, et erzwingt die korrekte Signatur.

Vergessen Sie nicht, dass in C# die Func Schnittstelle (oder Klasse, ich habe es vergessen) wird geschrieben als Func<T, Result> während C++'s std::function wird geschrieben als result(T) .

Oh, außerdem, wenn Sie C++0x nicht haben, gibt es eine in TR1 und auch eine in Boost, so dass Sie leicht in der Lage sein sollten, darauf zuzugreifen.

4voto

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

Sie können alles, was aufrufbar und kopierbar ist, als Selektor verwenden.

3voto

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

Dies ist nicht der beste Weg, er ist nur am einfachsten zu schreiben und zu verstehen. Der richtige Weg ist, einen Funktor zu verwenden, damit er auch mit den Lambda-Ausdrücken von C++0x funktioniert.

3voto

Es gibt viele Möglichkeiten, je nachdem, was Sie tun wollen, wie generisch und wiederverwendbar die Lösung sein soll, welche Speicherbeschränkungen Sie in Kauf nehmen (z. B. kontinuierlich angelegter Speicher gegenüber einigen verknüpften Listen usw.). Hier ist jedoch ein einfaches Beispiel mit 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;
}

Es könnte viel einfacher sein, wenn man zum Beispiel annimmt, dass man nur High-Level-Container (keine Arrays) akzeptieren kann. Und Sie können Lambdas erhalten, indem Sie zum Beispiel C++0x einwerfen:

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

Sie haben die Wahl :-)

P.S.: Ja, und das std::function (das ist eigentlich Zeug aus Boost in pre-C++0x) kann verwendet werden, um Ihren Algorithmus nicht-Vorlage. Ansonsten schränkt es Sie nur in Bezug auf die "Selektoren" ein, die Sie übergeben können.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X