2017-11-16 15 views
0

Ich versuche, eine variadische Template-Klasse zu erstellen, die wie folgt aussieht zu binden:Erste nicht angemeldeten args, wenn sie versuchen std :: Funktion mit dem Parameter Pack

template <class... Args> 
class Message 
{ 
    private: 

     std::function<void(Args...)> _function; 
    public: 
    //stuff 

}; 

Ich mag eine Option haben, zu binden, um für den Anwender kann jede Funktion (mit entsprechenden Argumenten) binden, wenn er will. Ich habe diesen Ansatz versucht (diese Funktion ein Teil der Message Klasse ist, so Args... aus der Vorlage Argumente der Klasse kommt):

template<class T> 
    void Message::bindFunction(void(T::*function)(Args... args), T* ownerPtr) 
    { 
     //This doesn't work 
     _function = boost::bind(function,args, ownerPtr); 
    } 

jedoch die oben nicht funktioniert, ich habe versucht, zu erstellen das Tupel aus dem args in der bind Funktion, aber der Compiler sagen hält:

<args_0> undeclared identifier. 

(Wenn ich mehr Argumente übergebe es ändert)

aus der Sicht des Benutzers es soll wie folgt aussehen:

class Foo 
{ 
public: 

    void test2(int a, float b, double c) 
    { 
     std::cout << "bla"; 
    } 
}; 

int main() 
{ 
    Foo f; 

    Message<int,float,double> Mess; 

    Mess.bindFunction(&Foo::test2, &f); 
} 

Ich habe viele Artikel gesehen, die über die Weiterleitung der Parameter sprechen, aber die meisten Antworten verwirren mich mehr als ich bereits mit verschiedenen Vorlagenstrukturen, ohne sie zu erklären.

+4

Sie verwenden bereits C++ 11; Ein Lambda wäre einfacher als "boost :: bind" (oder "std :: bind", das ebenfalls existiert). – chris

+0

@chris Ich weiß über die Existenz von std :: bind, die Verwendung von boost :: bind war vorgesehen, da ich Funktoren für Memberfunktionen erstellen wollte. – Pins

+1

@Pins, 'std :: bind' funktioniert auch für Member-Funktionen. In der Tat ist es eines der Beispiele auf [cppreference] (http://en.cppreference.com/w/cpp/utility/functional/bind). Ungeachtet dessen ist das Verbinden von Variadics mit beiden entweder schmerzhaft, es sei denn, [std :: bind_front'] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0356r2.html) macht es durch. Es gibt ein [C++ Weekly] (https://www.youtube.com/watch?v=ZlHi8txU4aQ) Video zum Vermeiden von 'bind' mit lambdas, aber es klingt wie ein zusätzliches variadisches Vorlagenmaterial. – chris

Antwort

2

einen Lambda verwenden, weil sie viel schöner (Live Demo (C++11)):

template<class T> 
void bindFunction(void(T::*function)(Args...), T* ownerPtr) 
{ 
    _function = [function, ownerPtr](Args... args) 
    { 
     (ownerPtr->*function)(args...); 
    }; 
} 

Wir sind die Funktionszeiger und Zeiger auf Instanz der Klasse mit [function, ownerPtr] in der Lambda-Erklärung zu erfassen.

Das Lambda akzeptiert eine Liste von Argumenten, deren Typen in der Variadic-Vorlage für die Message-Klasse angegeben sind: Args... args.

Intern nennen wir die Funktion unserer Instanz mit der (ownerPtr->*function) Syntax, und dann in den Argumenten übergeben werden, indem die Parameter Pack erweitert (args...)

die Funktion aufrufen Sie so etwas schreiben könnte:

void callFunction(Args&&... args) 
{ 
    _function(std::forward<Args>(args)...); 
} 

Wir verwenden perfect forwarding, um unsere Argumente an das std::function<void(Args...)> Mitglied zu übergeben.

+0

Ich bin überrascht wie handlich das Lambda hier funktioniert, jetzt funktioniert es super, danke. – Pins

+2

@Pins: Es wäre außerordentlich schwierig mit 'bind' gewesen, weil ein variadischer Platzhalter nicht existiert [also müssten Sie Ihren eigenen schreiben] (https://stackoverflow.com/a/21193316/27678) – AndyG

Verwandte Themen