2012-12-10 10 views
6

Ich weiß, dass diese Frage schon einmal gestellt wurde, aber trotz eines ziemlich erfahrenen Coders verstehe ich nicht die Antworten und sehe keine Möglichkeit, auf diese vorherigen Fragen zu beantworten Klärung. Es gibt keinen "Antwort" -Link oder irgendetwas. Außerdem waren diese Fragen ziemlich alt. Also stelle ich die Frage erneut.Wie Überladen auf Std :: Funktion Signatur in C++

Ich habe eine Klasse, in der ich den + = Operator überbelaste. Ich mag eine Überlastung mit einem nackten Funktionszeiger nehmen, und die andere eine std :: Funktion zu übernehmen:

void operator+=(void (*handler)()); 
void operator+=(function<void (void *, T)> handler); 

Verbrauch:

MyClass a; 
a += [](){ DoSomething(); }; 
a += [](void *x, T y){ DoSomething(); }; 

Leider funktioniert der Code nicht kompilieren, da der Compiler nicht bestimmen kann, dass die zweite Überlast für den ersten + = Anruf ungeeignet ist.

Wie definiere ich meine Operator + = Mitgliedsfunktionen, um dieses Problem zu beheben? Ich möchte nicht ändern, wie die Operatoren verwendet werden (z. B. durch Verwendung einer expliziten Umwandlung). Ich möchte, dass sie wie oben gezeigt funktionieren.

Außerdem wäre es sehr nützlich sein, wie auch die folgenden Überlastung haben:

void operator+=(function<void()> handler); 

Aber auch hier kann ich nicht auf die Signatur der Funktion basiert Überlastung <> Vorlage.

dieses Themas finden Sie weitere Beispiele: Isn't the template argument (the signature) of std::function part of its type? (Ich habe versucht, die verschiedenen Lösungen in diesem Thread erwähnt Implementierung und keiner von ihnen würde kompilieren)

ich seit vielen Jahren habe in einer Reihe von Programmier Sprachen, aber meine C++ Fähigkeiten sind ein wenig eingerostet.

+0

die ** große ** Frage ist: __ was wirst du mit einem gegebenen Funktionszeiger oder 'std :: function' Objekt in deiner Überladung tun' operator + = '? __ – zaufi

+0

@zaufi: Ich sehe nicht wie das ist relevant, aber in Ordnung. Ich werde es zu einem std :: vector von Event-Handler-Funktionen hinzufügen. Ich implementiere im Wesentlichen .NET-Ereignisse in C++. Denn die Mächte, die still sind, haben nicht den Kopf darauf gedreht, dass Events und Properties - wie Closures - absolut essentielle Funktionalität für eine moderne Programmiersprache sind. –

+0

also, dann müssen Sie einen 'std :: vector' haben, der mit' std :: function' instanziiert ist, mit genau einem (ich schlage 'void()') vor ... Habe ich recht? – zaufi

Antwort

3

Code Nach funktioniert gut mit g ++ 4.7.2:

#include <functional> 
#include <iostream> 

template <typename T> 
typename std::enable_if<std::is_convertible<T, void(*)()>::value>::type 
foo(T&&) 
{ 
    std::cout << "foo(void(*)())" << std::endl; 
} 

void foo(std::function<void(void*,int)>) 
{ 
    std::cout << "foo(std::function<void(void*,int)>)" << std::endl; 
} 

int main() 
{ 
    foo([]{}); 
    foo([](void*,int){}); 
} 

Das Problem wird durch den Konstruktor std::function verursacht wird, die als template <class F> function(F); deklariert wird. Dieser Konstruktor akzeptiert alles als sein Argument. Wenn dieses Argument kein passendes operator() hat, wird während der Instanziierung des Konstruktors ein Fehler generiert.

Leider erfordert der Prozess der Überladungsauflösung keine Template-Funktionen, die instanziiert werden müssen. Der Compiler glaubt also, dass alles in einen beliebigen std::function-Typ konvertiert werden kann.

+0

Danke, ich habe es geschafft, das ohne Probleme in meine Klasse zu bekommen; obwohl die Dinge ein wenig haarig waren, als ich versuchte, T zu einer Funktion <> zu werfen. Scheint, ich muss es zuerst in eine Lücke (*)() werfen. Prost. –

+0

Ich denke, das Hauptproblem, das ich hatte, ist, dass ich noch nie von enable_if gehört habe und es nicht verstehe. Ich war mir vage bewusst, dass es helfen könnte (von Antworten auf ähnliche Fragen), wusste aber nicht, wie es verwendet wurde. Ich bin mir immer noch nicht sicher, wie es funktioniert. –

+0

Einige Informationen zu 'enable_if' finden Sie [hier] (http://www.boost.org/doc/libs/1_52_0/libs/utility/enable_if.html) und [hier] (http: //en.cppreference .com/w/cpp/types/enable_if). – hpsMouse

0

Ok, wenn Sie eine std::vector<std::function<void()>> haben, brauchen Sie nicht eine Menge Überlastungen. Der einfachste Weg besteht darin, ein Templat operator+= zu definieren, das möglicherweise mit std::enable_if "geschützt" ist und nur für aufrufbare Objekt- oder Funktionszeiger eingeschaltet werden soll. Das Übergeben von lambdas oder rohen Zeigern führt eine automatische Konvertierung für Sie durch (push_back/emplace_back). Passing std::function wird nur wie erwartet zuweisen.

+0

Sie vermissen den Punkt. Ich möchte sowohl "[]() {}" lambdas als auch "[] (void *, T) {}" lambdas zu meinem Vektor hinzufügen. Ich verpacke sie in eine gemeinsame Containerklasse und füge sie meinem Vektor hinzu. Der Versuch, mich davon zu überzeugen, dass ich keine anderen Lambda-Signaturen brauche, ist eine sinnlose Verschwendung von Zeit und mir. –