Hallo!

Ich werde mal versuchen etwas Licht in die Sache zu bringen.
PietCN hat geschrieben:Hier wäre der erste Punkt ob ich es bisher richtig verstanden habe: std::function ist quasi ein Funktions-Wrapper in dem ich Funktionen mit der entsprechenden Signatur "hineinpacken" kann.
Ja, das stimmt so.
PietCN hat geschrieben:Vielleicht gibt es ja eine Erklärung die man als Normalsterblicher auch versteht. Was genau geschieht mit dem Placeholder und wie genau kann man sich das vorstellen das ein weiteres Argument an eine Funktion "gebunden" wird.
Sehen wir uns ein etwas einfacheres Beispiel an. Ich habe mir eine Funktion geschrieben, die mir die Summe aller Teiler bis zu einer Obergrenze zurück gibt.
Also z.B. der Teiler 3 bis zur Obergrenze 20:
3+6+9+12+15+18 = 63
Teiler 5 bis zur Obergrenze 20:
5+10+15+20 = 50
Die naive und sehr ineffiziente Implementierung sieht so aus:
Code: Alles auswählen
int sum(int div, int n)
{
int s = 0;
for(int i = 0; i <= n; i++)
{
if(i % div == 0)
s += i;
}
return s;
}
Wir haben also 2 Parameter:
1) den Teiler div
2) die Obergrenze n
Unsere beiden Beispiele würden bei einem ganz normalen Aufruf so aussehen:
Code: Alles auswählen
std::cout << sum(3, 20) << std::endl;
std::cout << sum(5, 20) << std::endl;
Wir können nun optional Parameter unserer Funktion binden. Das funktioniert über die Funktion std::bind und verlangt lediglich die zu bindende Funktion (in diesem Fall sum) und alle Parameter der Funktion (in der normalen Reihenfolge).
Der einfachste Fall ist jener, in dem wir keinen Parameter binden. Wir müssen aber weiterhin Platzhalter für die Parameter angeben. Dabei steht std::placeholders::_1 für den ersten an die gebundene Funktion übergebenen Parameter, _2 für den zweiten Parameter usw. Geben wir die std::placeholders also in aufsteigender Reihenfolge an, ändert sich mal gar nix gegenüber dem direkten Funktionsaufruf von sum().
Code: Alles auswählen
auto sum_not_bound = std::bind(sum, std::placeholders::_1, std::placeholders::_2);
std::cout << sum_not_bound(3, 20) << std::endl; // _1 = 3, _2 = 20 => sum(3, 20);
std::cout << sum_not_bound(5, 20) << std::endl; // _1 = 5, _2 = 20 => sum(5, 20);
Wir können die Reihenfolge der Platzhalter aber auch beliebig ändern:
Code: Alles auswählen
auto sum_swapped_order = std::bind(sum, std::placeholders::_2, std::placeholders::_1);
std::cout << sum_swapped_order(20, 3) << std::endl; // _1 = 20, _2 = 3 => sum(3, 20);
std::cout << sum_swapped_order(20, 5) << std::endl; // _1 = 20, _2 = 5 => sum(5, 20);
std::bin ermöglicht es nun aber auch den Wert bestimmter Parameter zu fixieren. Anstatt eines Platzhalter wie std::placeholders::_1 verwenden wir also einen konkreten Wert. Im folgenden Beispiel legen wir den Wert des Parameters n auf 20 fest.
Code: Alles auswählen
auto sum_fixed_upper_bound = std::bind(sum, std::placeholders::_1, 20);
std::cout << sum_fixed_upper_bound(3) << std::endl; // _1 = 3 => sum(3, 20);
std::cout << sum_fixed_upper_bound(5) << std::endl; // _1 = 5 => sum(5, 20);
Wir können aber auch alle Parameter binden.
Code: Alles auswählen
auto sum_all_fixed_1 = std::bind(sum, 3, 20);
auto sum_all_fixed_2 = std::bind(sum, 5, 20);
std::cout << sum_all_fixed_1() << std::endl; // sum(3, 20);
std::cout << sum_all_fixed_2() << std::endl; // sum(5, 20);
Schlussendlich kann das Binden auch schrittweise - basierend auf einem vorherigen std::bind() Aufruf - passieren.
Code: Alles auswählen
auto sum_also_all_fixed_1 = std::bind(sum_fixed_upper_bound, 3);
auto sum_also_all_fixed_2 = std::bind(sum_fixed_upper_bound, 5);
std::cout << sum_also_all_fixed_1() << std::endl; // sum(3, 20);
std::cout << sum_also_all_fixed_2() << std::endl; // sum(5, 20);
Hier nochmal der gesamte Code falls du damit herumspielen willst:
Code: Alles auswählen
#include <iostream>
#include <functional>
int sum(int div, int n)
{
int s = 0;
for(int i = 0; i <= n; i++)
{
if(i % div == 0)
s += i;
}
return s;
}
int main()
{
std::cout << sum(3, 20) << std::endl;
std::cout << sum(5, 20) << std::endl;
std::cout << std::endl;
auto sum_not_bound = std::bind(sum, std::placeholders::_1, std::placeholders::_2);
std::cout << sum_not_bound(3, 20) << std::endl;
std::cout << sum_not_bound(5, 20) << std::endl;
std::cout << std::endl;
auto sum_swapped_order = std::bind(sum, std::placeholders::_2, std::placeholders::_1);
std::cout << sum_swapped_order(20, 3) << std::endl;
std::cout << sum_swapped_order(20, 5) << std::endl;
std::cout << std::endl;
auto sum_fixed_upper_bound = std::bind(sum, std::placeholders::_1, 20);
std::cout << sum_fixed_upper_bound(3) << std::endl;
std::cout << sum_fixed_upper_bound(5) << std::endl;
std::cout << std::endl;
auto sum_all_fixed_1 = std::bind(sum, 3, 20);
auto sum_all_fixed_2 = std::bind(sum, 5, 20);
std::cout << sum_all_fixed_1() << std::endl;
std::cout << sum_all_fixed_2() << std::endl;
std::cout << std::endl;
auto sum_also_all_fixed_1 = std::bind(sum_fixed_upper_bound, 3);
auto sum_also_all_fixed_2 = std::bind(sum_fixed_upper_bound, 5);
std::cout << sum_also_all_fixed_1() << std::endl;
std::cout << sum_also_all_fixed_2() << std::endl;
return 0;
}
Kommen wir nun auf deine Beispiele zurück.
PietCN hat geschrieben:Code: Alles auswählen
void repair(int points) { mHitpoints =+ points;}
// ...
data[Pickup::HealtRefill].action = std::bind(&Aircraft::repair, std::placeholder::_1, 25); //und hier ist nun die krux... es funktioniert aber warum ^^
Du erstellst dir hier einen Wrapper für deine Methode Aircraft::repair(), in dem der Parameter points fix auf 25 gesetzt ist. std::placeholder::_1 steht hier für den this-Zeiger, der implizit an jede Objekt-Methode übergeben wird.
PietCN hat geschrieben:Code: Alles auswählen
data[Pickup::FireRate].action = std::bind(&Aircraft::increasFireRate, std::placeholder::_1);
Du bindest hier deine Methode Aircraft::increaseFireRate(), jedoch ohne Parameter fix zu setzen. Es wird lediglich der Placeholder für this gesetzt. Das hat auf den ersten Blick nicht viel Sinn, allerdings ist das praktisch wenn du dir deine PickupData-Struktur ansiehst:
PietCN hat geschrieben:Code: Alles auswählen
struct PickupData {
Textures::ID textures;
std::functional<void(Aircraft&) action;
}
Du kannst auf action also beliebige Funktionen setzen, die nur eine Referenz auf ein Aircraft-Objekt übergeben bekommen. Ich hätte an dieser Stelle ehrlich gesagt auf einen Zeiger gehofft, aber vielleicht kannst du uns ja einen Aufruf von so einer action zeigen
Konnte ich dir weiterhelfen?