Freigeben über


Dieser Artikel wurde maschinell übersetzt.

C++

Funktionale Programmierung in C++

David Cravey

C++ ist eine multiparadigm, Systemebene, die High-Level Abstraktionen mit sehr bietet niedrig (oft null) Laufzeit Kosten.Die häufig im Zusammenhang mit C++ Paradigmen sind prozessuale, objektorientierte und generische Programmierung.Da C++ hervorragende Werkzeuge für die High-Level Programmierung bietet, ist sogar funktionalen Stil Programmierung durchaus sinnvoll.

Mit funktionalen Stil Programmierung meine ich nicht, dass die Programmierung ist streng funktionale, eben jene es einfach zu bedienen viele funktionale Bausteine in C++ ist.Dieser Artikel konzentriert sich auf eines der wichtigsten funktionalen Programmierung Konstrukte: Arbeiten mit Werten im Gegensatz zu Identitäten.Ich werde erläutern, die starke Unterstützung C++ hatte schon immer für die Arbeit mit Werten, dann zeigen, wie der neue C++-11-Standard erweitert diese Unterstützung mit Lambda-Ausdrücke.Schließlich werde ich eine Methode des Arbeitens mit unveränderlichen Datenstrukturen einführen, die die Geschwindigkeit beibehält, die für C++ bekannt ist, und bietet gleichzeitig den Schutz, den funktionale Sprachen lange genossen haben.

Werte vs..Identitäten

Lassen Sie mich zunächst erklären, was ich meine, durch die Arbeit mit Werten anstelle von Identitäten.Einfache Werte wie 1, 2 und 3 sind einfach zu erkennen.Ich könnte auch sagen, dass 1, 2 und 3 Konstante Integer-Werte sind.Das wäre überflüssig, jedoch weil alle Werte eigentlich Konstanten sind und die Werte selbst nie ändern (1 ist immer 1 und 1 werden nie 2).Auf der anderen Seite kann eine Identität zugeordnete Wert ändern (X könnte jetzt gleich 1 ist, aber es könnte gleich 2 weiter unten).

Leider ist es einfach, Werte und Werttypen zu verwechseln.Werttypen werden um von Wert und nicht als Verweis übergeben.Obwohl ich hier auf die Werte und nicht der Mechanismus verwenden oder kopieren sie konzentrieren wollen, ist es nützlich zu sehen, wie Werttypen Teil des Weges gehen in das Konzept der Werte im Vergleich zu Identitäten beibehalten.

Der Code in Abbildung 1 veranschaulicht eine einfache Verwendung eines Werttyps.

Abbildung 1 mit einen Werttyp

void Foo()
{
  for (int x = 0; x < 10; ++x)
  {
    // Call Bar, passing the value of x and not the identity
    Bar(x);
  }
}
void Bar(int y)
{
  // Display the value of y
  cout << y << " ";
  // Change the value that the identity y refers to
  // Note: This will not affect the value that the variable x refers to
  y = y + 1;
}
// Outputs:
// 0 1 2 3 4 5 6 7 8 9

Mit nur eine kleine Änderung, kann die Variable y ein Verweistyp werden — die drastisch verändert, der Beziehung zwischen x und y, wie im Abbildung 2.

Abbildung 2 unter Verwendung eines Verweistyps

void Foo()
{
  for (int x = 0; x < 10; ++x)
  {
    // Call Bar, passing the identity of x
    Bar(x);
  }
}
void Bar(int& y)
{
  // Display the value of y
  cout << y << " ";
  // Change the value that the identity y refers to
  // Note: This will affect the variable x
  y = y + 1;
}
// Outputs:
// 0 2 4 6 8

Als Abbildung 3 zeigt, C++ bietet auch den const Modifizierer, der verhindert, dass des Programmierers die Änderungen an einer Variablen und so weiter bewahrt das Konzept eines Wertes. (Wie bei den meisten Dingen in C++, jedoch gibt es zumindest eine Möglichkeit, diesen Schutz zu besiegen. Weitere Informationen nachschlagen Const_cast, das soll für die Arbeit mit älteren Code, die nicht "Const korrekt.")

Abbildung 3 der const Modifizierer

void Foo()
{
  for (int x = 0; x < 10; ++x)
  {
    // Call Bar, passing the identity of x,
    // yet the value of x will be protected by the const
    Bar(x);
  }
}
void Bar(const int& y)
{
  // Use the value of y
  cout << y << " ";
  // Attempt to change the value of what the identity y refers to
  y = y + 1; // This line will not compile because y is const!
}

Hinweis: in Abbildung 3 , dass obwohl y als Verweis übergeben wird, der y-Wert zur Kompilierzeit einen const Modifizierer geschützt ist. Dies gibt C++-Programmierern eine sehr effiziente Methode der Übergabe großer Objekte während der Arbeit mit ihren Werten im Gegensatz zu ihrer Identität.

Mit dem const Modifizierer hat C++ unveränderlichen Daten-Typen, die denen meisten funktionalen Programmiersprachen ähneln. Bewältigung dieser Unveränderlichen Datentypen ist jedoch schwierig. Ist darüber hinaus nicht tief (voll) Kopien großer Objekte für jede kleine Änderung effizient. Dennoch sollte klar sein, dass C++-Standardbibliothek immer ein Konzept hatte der Arbeit mit Werten (auch wenn es kein sehr reines Konzept).

Beachten Sie, dass die Unterstützung für Werttypen auf benutzerdefinierte Typen durch Kopierkonstruktoren und Zuweisungsoperatoren erweitert. C++-Kopierkonstruktoren und Zuweisungsoperatoren können benutzerdefinierte Typen um eine tiefe Kopie des Objekts. Denken Sie daran, dass während Kopierkonstruktoren C++ implementiert werden können, um eine flache Kopie zu machen, müssen Sie sicherstellen, dass der Wertsemantik beibehalten werden.

C++ 11 Unterstützung für funktionale-Style-Programmierung

C++ 11 bringt eine Reihe neuer Tools für die Programmierung der funktionalen Stil. Vielleicht am wichtigsten ist, hat C++ jetzt Unterstützung für Lambda-Ausdrücke (auch bekannt als Verschlüsse oder anonyme Funktionen). Lambda-Ausdrücke können Sie Ihren Code in Wege zu komponieren, die praktische vor nicht gewesen. Diese Funktionalität war zuvor über Funktoren, die kraftvoll, aber weniger praktische Verwendung sind verfügbar. (Tatsächlich, C++ Lambda-Ausdrücke schreiben anonyme Funktoren hinter den Kulissen.) Abbildung 4 zeigt, wie Lambda-Ausdrücke unseres Codes mit einem einfachen Beispiel verbessert haben, die die C++-Standardbibliothek (STL) verwendet.

Abbildung 4 mithilfe von Lambda-Ausdrücke

void Foo()
{
  vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  for_each(begin(v), end(v), [](int i) {
    cout << i << " ";
  });
}
// Outputs:
// 1 2 3

In diesem Fall gilt die Funktion For_each einen Lambda-Ausdruck für jedes Element eines Vektors. Es ist wichtig zu beachten, dass C++ Lambda-Ausdrücke gebrauchte Inline wenn möglich sein entworfen wurden; Lambda-Ausdrücke können daher so schnell wie handgefertigte Code ausgeführt werden.

C++ nur eine von vielen imperativen Sprachen ist, die jetzt Lambda-Ausdrücke, zwar macht C++ Lambda-Ausdrücke besonders, dass (ähnlich wie funktionale Programmiersprachen) können sie das Konzept der Arbeit mit Werten im Gegensatz zu Identitäten bewahren. Während funktionale Programmiersprachen hierzu durch Variablen unveränderlich machen handelt C++ es um die Kontrolle über die Gefangennahme. Sehen Sie sich den Code in Abbildung 5 an.

Abbildung 5 Erfassung durch Verweis

void Foo()
{
  int a[3] = { 11, 12, 13 };
  vector<function<void(void)>> vf;
  // Store lambdas to print each element of an array
  int ctr;
  for (ctr = 0; ctr < 3; ++ctr) {
    vf.push_back([&]() {
      cout << "a[" << ctr << "] = " << a[ctr] << endl;
    });   
  }
  // Run each lambda
  for_each(begin(vf), end(vf), [](function<void(void)> f) {
    f();
  });
}
// Outputs:
// a[3] = -858993460
// a[3] = -858993460
// a[3] = -858993460

In diesem Code ist alles Verweis erfasst, die das Standardverhalten für Lambda-Ausdrücke in anderen Sprachen ist. Noch durch Verweis erfassen erschwert Dinge, wenn die Variablen gefangen unveränderlich sind. Wenn Sie neu mit Lambda-Ausdrücke zu arbeiten, erwarten Sie wahrscheinlich die folgende Ausgabe aus dem Code:

a[0] = 11
a[1] = 12
a[2] = 13

Aber das ist nicht die Ausgabe, die Sie erhalten — und das Programm könnte sogar abstürzen. Dies ist, da die Variable Ctr Verweis erfasst wird, so dass alle die Lambda-Ausdrücke den endgültigen Wert der Ctr verwenden (d. h. 3, der Wert, der aus der for-Schleife zu einem Ende zu kommen) und dann auf das Array über seine Grenzen hinaus.

Es ist auch erwähnenswert, dass die Ctr-Variable von außerhalb des Lambda-Ausdrucks verwendet werden lebendig zu halten die for-Schleife hat die Ctr-Variable Deklaration von aufgehoben werden die for-Schleife. Zwar einige Sprachen die Werttypvariablen auf einen entsprechenden Bereich heben, die wirklich löst nicht das Problem, die ist, dass der Lambda-Ausdruck den Wert der Ctr im Gegensatz zu der Identität der Variable CTR muss. (Es gibt Workarounds für andere Sprachen, bei denen eine explizite Kopie einer temporären Variablen zu machen. Aber das macht es etwas unklar, was vor sich geht und es ist fehleranfällig, weil die ursprüngliche Variable auch erfasst wird und somit noch zur Verfügung steht.)

Als Abbildung 6 zeigt, C++ bietet eine einfache Syntax um einfache Steuerung der Lambda-Capture, zu ermöglichen, das Konzept der Arbeit mit Werte bleiben erhalten.

Abbildung 6-C++-Syntax für die Steuerung von Lambda erfassen

[]

Nicht alles erfassen

(genau, was ich im ersten Lambda-Beispiel wollte)

[&]

Erfassen Sie alles unter Bezugnahme

(traditionelle Lambda-Verhalten, aber nicht im Einklang mit funktionalen Programmierung Betonung Werte)

[=]

Alles von Wert zu erfassen

(während dies das Konzept der Werte bewahrt, schränkt sie die Nützlichkeit der Lambda-Ausdrücke; Es kann auch große Objekte kopieren teuer werden)

[& Ctr] Erfassen nur Ctr und Erfassen von Ctr unter Bezugnahme
[Ctr] Erfassen Sie nur Ctr und erfassen Ctr von Wert zu
[&, Ctr] Ctr von Wert und alles andere als Verweis zu erfassen
[=, & V] V durch einen Verweis und alles andere von Wert zu erfassen
[&, ctr1, ctr2] Erfassung von ctr1 und ctr2 von Wert und alles andere als Verweis

Ergibt sich aus Abbildung 6 , dass der Programmierer vollständigen Kontrolle hat über wie der Lambda-Ausdruck Variablen und Werte erfasst. Jedoch während dadurch das Konzept der Arbeit mit Werten bleibt erhalten, ändert es nichts an Arbeiten mit komplexen Daten Strukturen als Werte effizienter zu machen.

Unveränderliche Datentypen

Was fehlt, sind die effiziente unveränderlich Datenstrukturen, die einige funktionalen Programmiersprachen haben. Diese Sprachen erleichtern unveränderlichen Daten-Strukturen, die effizient sogar, wenn Sie sehr groß sind, weil sie gemeinsame Daten teilen. Das Erstellen von Datenstrukturen in C++, die Daten gemeinsam ist trivial — Sie nur dynamisch Daten zuweisen und jede Datenstruktur enthält Zeiger auf die Daten. Leider ist es schwieriger, die Lebensdauer der freigegebene Variablen verwalten (aus diesem Grund Garbage Collectors populär geworden). Glücklicherweise C++ 11 bietet eine elegante Lösung für das Arbeiten mit gemeinsam genutzten Variablen durch die std::shared_ptr-Vorlage-Klasse, siehe Abbildung 7.

Abbildung 7 Austausch von Variablen

void Foo()
{
  // Create a shared int
  // (dynamically allocates an integer
  //  and provides automatic reference counting)
  auto sharedInt = make_shared<int>(123);
  // Share the integer with a secondShare
  // (increments the reference count)
  shared_ptr<int> secondShare(sharedInt);
  // Release the pointer to the first integer
  // (decrements the reference count)
  sharedInt = nullptr;
  // Verify the shared int is still alive
  cout << "secondShare = " << *secondShare << endl;
  // Shared int is automatically de-allocated
  // as secondShare falls out of scope and the reference
  // count falls to zero
}
// Outputs:
// secondShare = 123

Der Code in Abbildung 7 veranschaulicht die einfache Verwendung des std::shared_ptr und seine Helfer Funktion std::make_shared. Von std::shared_ptr erleichtert das Freigeben von Daten zwischen den Datenstrukturen ohne Angst Speicherverlust (solange Zirkelverweise vermieden werden). Beachten Sie, dass std::shared_ptr die grundlegenden Thread-Sicherheit Gewähr dafür bietet, und läuft schnell, weil es eine Sperre-freie Design verwendet. Jedoch verlängern nicht beachten, die die grundlegenden Threadsicherheit zu gewährleisten, bietet die std::shared_ptr auf das Objekt automatisch, denen es verweist. Dennoch garantiert std::shared_ptr, dass er nicht die Thread-Sicherheit Garantie des Objekts verringert, auf den er verweist. Unveränderliche Objekte bieten von Natur aus stark Thread-Sicherheit garantiert, da sobald sie erstellt sind sie nie ändern. (Tatsächlich, ändern sie nie in beobachtbaren Weise, die unter anderem eine entsprechenden Thread-Sicherheit-Garantie umfasst.) Wenn Sie eine std::shared_ptr mit einem unveränderlichen Objekt verwenden, hält die Kombination daher, das unveränderliche Objekt stark Thread-Sicherheit garantieren.

Kann ich jetzt leicht erstellen eine einfache unveränderliche Klasse, das potenziell Daten teilt, wie in Abbildung 8.

Abbildung 8 eine unveränderliche Klasse für den Datenaustausch

class Immutable
{
private:
  // Use a normal double, copying is cheap
  double d_;
  // Use a shared string, because strings can be very large
  std::shared_ptr<std::string const> s_;
public:
  // Default constructor
  Immutable()
    : d_(0.0),
      s_(std::make_shared<std::string const>(""))
  {}
  // Constructor taking a string
  Immutable(const double d, const string& s)
    : d_(d),
    s_(std::make_shared<std::string const>(s))
  {}
  // Move constructor
  Immutable(Immutable&& other)
    : s_()
  {
    using std::swap;
    swap(d_, other.d_);
    swap(s_, other.s_);
  }
  // Move assignment operator
  Immutable& operator=(Immutable&& other)
  {
    swap(d_, other.d_);
    swap(s_, other.s_);
    return *this;
  }
  // Use default copy constructor and assignment operator
  // Getter Functions
  double GetD() const
  {
    // Return a copy because double is small (8 bytes)
    return d_;
  }
  const std::string& GetS() const
  {
    // Return a const reference because string can be very large
    return *s_;
  }
  // "Setter" Functions (always return a new object)
  Immutable SetD(double d) const
  {
    Immutable newObject(*this);
    newObject.d_ = d;
    return newObject;
  }
  Immutable SetS(const std::string& s) const
  {
    Immutable newObject(*this);
    newObject.s_ = std::make_shared<std::string const>(s);
    return newObject;
  }
};

Der Code in Abbildung 8 ist ein bisschen lang, aber das meiste davon ist Standardcode für die Konstruktoren und Zuweisungsoperatoren. Die letzten beiden Funktionen sind der Schlüssel zu das Objekt unveränderlich. Beachten Sie, dass die SetS und SetD Methoden geben ein neues Objekt, das das ursprüngliche Objekt unverändert lässt. (Während der SetS und SetD Methoden wie Mitglieder ist bequem, es ist ein bisschen wie eine Lüge, weil sie tatsächlich das ursprüngliche Objekt ändern nicht. Eine saubere Lösung finden Sie unter der ImmutableVector in Zahlen 9 und 10.) Abbildung 11 zeigt die unveränderliche Klasse in Aktion.

Abbildung 9 mit der intelligenten ImmutableVector Template-Klasse

template <class ImmutableVector>
void DisplayImmutableVector(const char* name, const ImmutableVector& v)
{
  using namespace std;
  cout << name << ".Size() = " << v.Size()
     << ", " << name << "[] = { ";
  for (size_t ctr = 0; ctr < v.Size(); ++ctr) {
    cout << v[ctr] << " ";
  }
  cout << "}" << endl;
}
void ImmutableVectorTest1()
{
  // Create an ImmutableVector with a branching size of four
  ImmutableVector<int, 4> v;
  // Another ImmutableVector (we will take a copy of v at element 6)
  ImmutableVector<int, 4> aCopyOfV;
  // Push 16 values into the vector (this will create a two level tree).
// Note that the vector is being assigned to itself.
The
  // move constructor insures this is not very expensive, but
  // if a copy was made at any point the copy would remain
  // unchanged, but continue to share the applicable data with
  // the current version.
for (int ctr = 0; ctr < 10; ++ctr) {
    v = AppendValue(v, ctr);
    if (ctr == 6) aCopyOfV = v;
  }
  // Display the contents of the vectors
  DisplayImmutableVector("v", v);
  DisplayImmutableVector("aCopyOfV", aCopyOfV);
}
// Outputs:
// v.Size() = 10, v[] = { 0 1 2 3 4 5 6 7 8 9 }
// aCopyOfV.Size() = 7, aCopyOfV[] = { 0 1 2 3 4 5 6 }

Abbildung 10 Methoden für die Arbeit der ImmutableVector

void ImmutableVectorTest2()
{
  ImmutableVector<int, 4> v;
  v = AppendValue(v, 1);
  v = AppendValue(v, 2);
  v = AppendValue(v, 3);
  int oldValue = v.Back();
  auto v1 = TruncateValue(v);
  auto v2 = SubstituteValueAtIndex(v, 0, 3);
  auto v3 = GenerateFrom(v, [](ImmutableVector<int, 4>::MutableVector& v) {
    v[0] = 4;
    v[1] = 5;
    v[2] = 6;
    v.PushBack(7);
    v.PushBack(8);
  });
  auto v4 = GenerateFrom(v3, [](ImmutableVector<int, 4>::MutableVector& v4) {
    using namespace std;
    cout << "Change v4 by calling PopBack:" << endl;
    cout << "x = v4.PopBack()" << endl;
    int x = v4.PopBack();
    cout << "x == " << x << endl;
    cout << endl;
  });
  // Display the contents of the vectors
  DisplayImmutableVector("v", v);
  DisplayImmutableVector("v1", v1);
  DisplayImmutableVector("v2", v2);
  DisplayImmutableVector("v3", v3);
  DisplayImmutableVector("v4", v4);
}
// Outputs:
//    Change v4 by calling PopBack:
//    x = v4.PopBack()
//    x == 8
//   
//    Resulting ImmutableVectors:
//    v.Size() = 3, v[] = { 1 2 3 }
//    v1.Size() = 2, v1[] = { 1 2 }
//    v2.Size() = 3, v2[] = { 3 2 1 }
//    v3.Size() = 5, v3[] = { 4 5 6 7 8 }
//    v4.Size() = 4, v4[] = { 4 5 6 7 }

Abbildung 11 die unveränderliche Klasse in Aktion

using namespace std;
void Foo()
{
  // Create an immutable object
  double d1 = 1.1;
  string s1 = "Hello World";
  Immutable a(d1, s1);
  // Create a copy of the immutable object, share the data
  Immutable b(a);
  // Create a new immutable object
  // by changing an existing immutable object
  // (Note the new object is returned)
  string s2 = "Hello Other";
  Immutable c = a.SetS(s2);
  // Display the contents of each object
  cout << "a.GetD() = " << a.GetD() << ", "
    << "a.GetS() = " << a.GetS()
    << " [address = " << &(a.GetS()) << "]" << endl;
  cout << "b.GetD() = " << b.GetD() << ", "
    << "b.GetS() = " << b.GetS()
    << " [address = " << &(b.GetS()) << "]" << endl;
  cout << "c.GetD() = " << c.GetD() << ", "
    << "c.GetS() = " << c.GetS()
    << " [address = " << &(c.GetS()) << "]" << endl;
}
// Outputs:
// a.GetD() = 1.1, a.GetS() = Hello World [address = 008354BC]
// b.GetD() = 1.1, b.GetS() = Hello World [address = 008354BC]
// c.GetD() = 1.1, c.GetS() = Hello Other [address = 008355B4]

Beachten Sie, dass Objekt b teilt die gleiche Zeichenfolge als Objekt ein (beide Zeichenfolgen sind auf die gleiche Adresse).Hinzufügen von zusätzliche Feldern zugeordneten Getter und Setter ist trivial.Obwohl dieser Code gut ist, ist es etwas schwieriger, mit Containern zu skalieren, wenn Sie effizient sind.Beispielsweise könnte eine naive ImmutableVector Verwalten einer Liste von freigegebenen Zeiger, jedes Element des Arrays darstellt.Wenn die naiven, die unveränderlich-Vector geändert wird, die das gesamte Array von freigegebenen Zeigern dupliziert werden müssten, würde dass zusätzliche Kosten als jedes Shared_ptr Element des Arrays seine Verweiszähler brauchen um erhöht werden.

Es gibt eine Technik, aber ermöglicht, die die Datenstruktur zu teilen die meisten seiner Daten und minimieren die Vervielfältigung.Diese Technik verwendet einen Baum in irgendeiner Form zur Vervielfältigung nur Knoten erforderlich, die direkt von einer Änderung betroffen sind.Abbildung 12zeigt einen Vergleich von einem naiven ImmutableVector und eine intelligente ImmutableVector.

Comparing Naïve and Smart ImmutableVectorsAbbildung 12 Vergleich der naiven und intelligente ImmutableVectors

Dieser Baum-Technik skaliert gut: wächst die Anzahl der Elemente, wird minimiert, die Prozent-Struktur, die dupliziert werden muss.Darüber hinaus können Sie durch Anpassen des Verzweigungen Faktors (so dass jeder Knoten verfügt über mehr als zwei Kinder), ein Gleichgewicht in Speicher-Overhead und Knoten-Wiederverwendung erreichen.

Ich entwickelte eine intelligente ImmutableVector-Vorlage-Klasse.Abbildung 9 zeigt, wie Sie meine ImmutableVector-Klasse verwenden können.(Wie bereits erwähnt, um die unveränderliche Natur der ImmutableVector klarer die Benutzer zu machen, verwendet ImmutableVector statische Memberfunktionen für alle Aktionen, die neue Versionen zu erzeugen.)

Für nur-Lese-Aktionen kann der Vektor viel wie eine reguläre Vector verwendet werden.(Beachten Sie, dass für dieses Beispiel habe nicht ich Iteratoren implementiert, aber dabei also ziemlich trivial muß.) Für Schreibzugriff Aktionen zurück die statischen Methoden AppendValue und TruncateValue eine neue ImmutableVector, damit das ursprüngliche Objekt beibehalten.Unfortu-Nately, dies ist nicht angemessen für das Array Indexoperator, also ich den Indexoperator Array schreibgeschützt machte (das heißt, es gibt einen const Verweis) und eine statische SubstituteValueAtIndex-Methode.Es wäre allerdings schön, um eine große Anzahl von Änderungen mithilfe der Indexoperator Array in einem Block des Codes machen zu können.Um dies zu erleichtern, bietet ImmutableVector eine statische GenerateFrom-Methode, die einen Lambda-Ausdruck (oder jede andere Funktor) annimmt.Der Lambda-Ausdruck übernimmt wiederum einen Verweis auf MutableVector als Parameter, wodurch der Lambda-Ausdruck auf eine temporäre MutableVector arbeiten, die wie ein normaler Std:: Vector beliebig verändert werden können.Das Beispiel in Abbildung 10 zeigt die verschiedenen Methoden für die Arbeit der ImmutableVector.

Die Schönheit der statischen Methode GenerateFrom ist, dass der Code geschrieben werden kann imperative Weise, während resultierende ein unveränderliches Objekt, das sicher gemeinsam genutzt werden können.Beachten Sie, dass die Generieren von statische Methode unbefugten Zugriff auf die zugrunde liegenden ImmutableVector verhindert durch Deaktivierung der MutableVector es an den Lambda-Ausdruck übergeben, sobald der Lambda-Ausdruck beendet.Bitte beachten Sie auch, dass ImmutableVector bietet eine starke Threadsicherheit zu gewährleisten, die Hilfsklasse MutableVector nicht (und nur lokal innerhalb der Lambda-Ausdruck, um andere Threads nicht herumgereicht verwendet werden soll).Meine Implementierung optimiert auch für mehrere Änderungen innerhalb der Change-Methode, so dass es minimal Umstrukturierung auf die temporäre Struktur, die eine schöne Leistungssteigerung gibt auftreten.

Zusammenfassung

Dieser Artikel gibt Ihnen nur einen Vorgeschmack auf die Verwendung von funktionalen Stil Programmierung in C++-Code.Darüber hinaus bringen C++ 11 Features wie Lambda-Ausdrücke einen Hauch von funktionalen Stil Programmierung unabhängig davon das Paradigma verwendet.

David Cravey ist ein Visual C++-MVP, genießt Programmierung in C++ vielleicht ein bisschen zu viel.Sie finden ihn auf lokale C++-Benutzergruppen und Universitäten präsentiert.Tagsüber genießt er die Arbeit bei NCR, durch TEKsystems in Fort Worth, Texas.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Giovanni Dicanio, Stephan T.Lavavej, Angel Hernández Matos, Alf s.Steinbach und David Wilkinson