
Datenkunde
Wenn ich diese Funktion definiere,
template<class A>
set<A> test(const set<A>& input) {
return input;
}
Ich kann es mit aufrufen test(mySet)
an anderer Stelle im Code, ohne den Vorlagentyp explizit definieren zu müssen. Wenn ich jedoch die folgende Funktion verwende:
template<class A>
set<A> filter(const set<A>& input,function<bool(A)> compare) {
set<A> ret;
for(auto it = input.begin(); it != input.end(); it++) {
if(compare(*it)) {
ret.insert(*it);
}
}
return ret;
}
Wenn ich diese Funktion mit aufrufe filter(mySet,[](int i) { return i%2==0; });
Ich bekomme folgenden Fehler:
Fehler: keine passende Funktion für den Aufruf von ‘filter(std::set&, main()::)’
Allerdings alle diese Versionen tun Arbeit:
std::function<bool(int)> func = [](int i) { return i%2 ==0; };
set<int> myNewSet = filter(mySet,func);
set<int> myNewSet = filter<int>(mySet,[](int i) { return i%2==0; });
set<int> myNewSet = filter(mySet,function<bool(int)>([](int i){return i%2==0;}));
Warum kann c++11 den Vorlagentyp nicht erraten, wenn ich die Lambda-Funktion direkt in den Ausdruck einfüge, ohne direkt eine zu erstellen std::function
?
BEARBEITEN:
Auf Anraten von Luc Danton in den Kommentaren gibt es hier eine Alternative zu der Funktion, die ich zuvor hatte und bei der die Vorlagen nicht explizit übergeben werden müssen.
template<class A,class CompareFunction>
set<A> filter(const set<A>& input,CompareFunction compare) {
set<A> ret;
for(auto it = input.begin(); it != input.end(); it++) {
if(compare(*it)) {
ret.insert(*it);
}
}
return ret;
}
Diese kann per angerufen werden set<int> result = filter(myIntSet,[](int i) { i % 2 == 0; });
ohne die Vorlage zu benötigen.
Der Compiler kann sogar bis zu einem gewissen Grad die Rückgabetypen erraten, indem er das neue Schlüsselwort decltype und die Syntax des neuen Funktionsrückgabetyps verwendet. Hier ist ein Beispiel, das einen Satz in eine Karte umwandelt, wobei eine Filterfunktion und eine Funktion verwendet werden, die die Schlüssel basierend auf den Werten generiert:
template<class Value,class CompareType,class IndexType>
auto filter(const set<Value>& input,CompareType compare,IndexType index) -> map<decltype(index(*(input.begin()))),Value> {
map<decltype(index(*(input.begin()))),Value> ret;
for(auto it = input.begin(); it != input.end(); it++) {
if(compare(*it)) {
ret[index(*it)] = *it;
}
}
return ret;
}
Es kann auch aufgerufen werden, ohne das Template direkt zu verwenden, as
map<string,int> s = filter(myIntSet,[](int i) { return i%2==0; },[](int i) { return toString(i); });

David Rodríguez – Dribeas
Das Problem liegt in der Natur von Lambdas. Sie sind Funktionsobjekte mit einem festen Satz von Eigenschaften gemäß dem Standard, aber sie sind es nicht eine Funktion. Der Standard legt fest, dass Lambdas umgewandelt werden können std::function<>
mit den genauen Arten von Argumenten und, falls sie keinen Zustand haben, Funktionszeigern.
Aber das bedeutet nicht, dass ein Lambda ein ist std::function
noch ein Funktionszeiger. Sie sind einzigartige Arten der Umsetzung operator()
.
Die Typableitung hingegen wird nur exakte Typen ableiten, ohne Konvertierungen (außer konstanten/flüchtigen Qualifikationen). Denn das Lambda ist nicht a std::function
der Compiler kann den Typ im Aufruf nicht ableiten: filter(mySet,[](int i) { return i%2==0; });
irgendeine zu sein std::function<>
Instanziierung.
Wie bei den anderen Beispielen konvertieren Sie im ersten das Lambda in den Funktionstyp und übergeben diesen dann. Der Compiler kann dort den Typ ableiten, wie im dritten Beispiel, wo die std::function
ist ein rvalue (temporär) desselben Typs.
Wenn Sie den instanziierenden Typ angeben int
Für die Vorlage, zweites Arbeitsbeispiel, kommt der Abzug nicht ins Spiel, der Compiler verwendet den Typ und konvertiert dann das Lambda in den entsprechenden Typ.

Nawaz
Vergiss deinen Fall. da dies zu komplex für eine Analyse ist.
Nehmen Sie dieses einfache Beispiel:
template<typename T>
struct X
{
X(T data) {}
};
template<typename T>
void f(X<T> x) {}
Ruf jetzt an f
als:
f(10);
Hier könnte man versucht sein, das zu denken T
wird davon abgeleitet int
und Daher sollte der obige Funktionsaufruf funktionieren. Nun, das ist nicht der Fall. Um die Sache einfach zu halten, stellen Sie sich vor, dass es eine gibt Ein weiterer Konstruktor, der dauert int
als:
template<typename T>
struct X
{
X(T data) {}
X(int data) {} //another constructor
};
Was jetzt T
sollte abgeleitet werden, wenn ich schreibe f(10)
? Brunnen, T
könnten irgendein Art.
Beachten Sie, dass es viele andere solche Fälle geben könnte. Nehmen Sie zum Beispiel diese Spezialisierung:
template<typename T>
struct X<T*> //specialized for pointers
{
X(int data) {};
};
Was jetzt T
sollte für den Anruf abgeleitet werden f(10)
? Jetzt scheint es noch schwieriger zu sein.
Es handelt sich daher um einen nicht ableitbaren Kontext, der erklärt, warum Ihr Code nicht funktioniert std::function
Das ist ein identischer Fall – sieht nur an der Oberfläche komplex aus. Beachten Sie, dass Lambdas sind nicht typ std::function
– Sie sind im Grunde genommen Instanzen von Compiler-generierten Klassen (d. h. sie sind Funktoren von unterschiedlich Typen als std::function
).
Wenn wir haben:
template <typename R, typename T>
int myfunc(std::function<R(T)> lambda)
{
return lambda(2);
}
int r = myfunc([](int i) { return i + 1; });
Es wird nicht kompiliert. Aber wenn Sie vorher erklären:
template <typename Func, typename Arg1>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr) -> decltype((*func)(*arg1));
template <typename Func>
int myfunc(Func lambda)
{
return myfunc<int, decltype(getFuncType<Func, int>())>(lambda);
}
Sie können Ihre Funktion problemlos mit einem Lambda-Parameter aufrufen.
Hier gibt es 2 neue Codeteile.
Zuerst haben wir eine Funktionsdeklaration, die nur nützlich ist, um einen Funktionszeigertyp im alten Stil basierend auf gegebenen Vorlagenparametern zurückzugeben:
template <typename Func, typename Arg1>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr) -> decltype((*func)(*arg1)) {};
Zweitens haben wir eine Funktion, die ein Vorlagenargument verwendet, um unseren erwarteten Lambda-Typ zu erstellen, der „getFuncType“ aufruft:
template <typename Func>
int myfunc(Func lambda)
{
return myfunc<int, decltype(getFuncType<Func, int>())>(lambda);
}
Mit den richtigen Template-Parametern können wir jetzt das echte „myfunc“ aufrufen. Vollständiger Code wird sein:
template <typename R, typename T>
int myfunc(std::function<R(T)> lambda)
{
return lambda(2);
}
template <typename Func, typename Arg1>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr) -> decltype((*func)(*arg1)) {};
template <typename Func>
int myfunc(Func lambda)
{
return myfunc<int, decltype(getFuncType<Func, int>())>(lambda);
}
int r = myfunc([](int i) { return i + 1; });
Sie können jede Überladung für „getFuncType“ so deklarieren, dass sie mit Ihrem Lambda-Parameter übereinstimmt. Zum Beispiel:
template <typename Func, typename Arg1, typename Arg2>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr, Arg2* arg2 = nullptr) -> decltype((*func)(*arg1, *arg2)) {};
9931300cookie-checkC++11 leitet den Typ nicht ab, wenn std::function- oder Lambda-Funktionen beteiligt sindyes
Unabhängig von Ihrer Frage, aber Sie wissen, dass Ihre
filter
ist im Wesentlichen äquivalent zu einer nicht generischen Version vonstd::copy_if
nicht wahr?– Jerry Sarg
3. April 2012 um 17:45 Uhr
Ah, std::copy_if war mir nicht bekannt, danke für den Hinweis. Dies ist jedoch Teil einer größeren Gruppe von 4 Funktionen, eine, die set => map beim Filtern konvertiert, und ich sehe keine Möglichkeit, dies mit copy_if zu implementieren und dem Benutzer zu ermöglichen, Schlüssel mit den Werten im Set zu erstellen. Aus Gründen der Konsistenz in der Verwendung entscheide ich mich dafür, dies auf diese Weise zu tun.
– Datenkunde
3. April 2012 um 18:27 Uhr
Für die Aufzeichnung, wenn Sie einen Funktor akzeptieren möchten, ist es normalerweise idiomatisch, ihn zu einem allgemeinen Vorlagenparameter zu machen, dh
template<typename A, typename Predicate> set<A> filter(set<A> const& input, Predicate compare);
. Wie Sie gerade gesehen haben,std::function
funktioniert nicht, um zu dokumentieren, dass das übergebene Prädikat eine Signaturübereinstimmung haben solltebool(A)
; es gibt andere Möglichkeiten, das zu tun. Weiterhin gibt es andere Nachteile bei der Verwendungstd::function
als Funktionsargument.– Luc Danton
4. April 2012 um 3:11 Uhr