2016-12-02 1 views
1

Ich möchte eine Klasse erstellen, als wenn instanziiert, akzeptiert eine variable Anzahl von Funktionsreferenzen, die keine Signaturen zuvor bekannt haben. Hier ist ein Beispiel, das fast das tut, was ich will:Eine Klasse, die Variablenfunktionsreferenzen unbekannter Signatur akzeptieren kann

// To show the function refs being used 
void p(int arg) { cout << "int " << arg << endl; } 
void p(string arg) { cout << "string " << arg << endl; } 
void p(int arg1, int arg2) { cout<<"int/int "<<arg1<<arg2<<endl; } 
void p(int arg1, string arg2) { cout<<"int/string "<<arg1<<arg2<<endl; } 

class foo { 
    public: 
    // CTOR takes variadic function refs 
    template <typename... Args> 
    foo(Args... args) { p(args()...); } 
    // "args()..." requires supplied functions to take no parameters 
    // but makes no requirement on their return values. 
}; 

// Using lambdas, but free functions, std::bind(), etc. work too 
foo i([]{return 1;});    // prints "int 1" 
foo s([]{return string("one");}); // prints "string one" 
foo b([]{return 2;}, 
     []{return string("two");}); // prints "int/string 2two" 

Was kann ich nicht sehen, wie dieses Problem zu beheben, so dass die als Argumente geliefert Funktionen nicht im Konstruktor ausgewertet werden. Ich möchte, dass der Anruf zu p(args()...) zu einem späteren Zeitpunkt erfolgt, durch eine andere Methode in foo. Deshalb kann foo nicht so einfach erstellt werden wie foo i(1): Die Argumentfunktion (en) müssen später und mehrfach aufgerufen werden, nicht nur einmal, wenn das Objekt erstellt wird (und sie sind komplexer, als nur ein Konstante).

Das Problem scheint zu speichern Referenzen auf den Konstruktor Parameter später aufgerufen werden, wenn die Klasse nicht wissen, wie viele oder welche Signatur diese Parameter haben. Irgendwie müssen die Argumente Teil einer Klassenvorlage und nicht nur einer Konstruktorvorlage sein, aber wie?

Wenn die übergebenen Funktionen alle die gleiche Signatur haben, dann könnte man eine Klassenvorlage mit einem Nicht-Typ-Parametern verwenden und die Funktionen als Vorlage Argumente liefern:

template <int (&...Arg)()> 
class bar { 
    public: 
    bar() { p(Arg()...); } 
    other() { p(Arg()...); } // Use in any method 
}; 

int one() { return 1; } 
int two() { return 2; } 
bar<one> bi;  // Prints "int 1" 
bar<one, two> bii; // Prints "int/int 12" 

Dies erfordert jedoch alle Argumente Funktionen sein that return int und funktioniert auch nicht mit lambdas, da sie keine Template-Argumente sein können.

+0

ich diese Zeilen lesen und Lisp denke. –

Antwort

2

Sie können ein Lambda und ein std::function verwenden, um das zu tun.
Beachten Sie, dass ein Lambda ein Parameterpaket erfassen und (sagen wir mal) es später entpacken kann.
Es folgt ein minimales, Arbeitsbeispiel:

#include<iostream> 
#include<functional> 

void p(int arg) { std::cout << "int " << arg << std::endl; } 
void p(std::string arg) { std::cout << "string " << arg << std::endl; } 
void p(int arg1, int arg2) { std::cout<<"int/int "<<arg1<<arg2<<std::endl; } 
void p(int arg1, std::string arg2) { std::cout<<"int/string "<<arg1<<arg2<<std::endl; } 

class foo { 
public: 
    template <typename... Args> 
    foo(Args... args): func{[args...](){ p(args()...); }} {} 

    void operator()() { func(); } 

private: 
    std::function<void()> func; 
}; 

int main() { 
    // create your objects... 
    foo i([]{return 1;}); 
    foo s([]{return std::string("one");}); 
    foo b([]{return 2;}, []{return std::string("two");}); 
    // ... and use them later 
    i(); 
    s(); 
    b(); 
} 
+0

Ich sehe, das Lambda ermöglicht das Packen/Entpacken des Parameterpakets in ein Klassenmitglied, dessen Typ nicht vom Parameterpaket abhängig ist. – TrentP

+0

Meine tatsächliche Verwendung ist komplexer, und das Parameterpaket ist nur ein Teil der Argumente für eine Funktion, z. 'make_shared (" name ", args() ..., index ++)' Das Lambda kann _just_ den 'args() ...' Teil nicht so erfassen, wie ich möchte. Die Lösung scheint zu sein, das Lambda zu erweitern, bis es einen vollständigen Ausdruck erzeugt, dessen Signatur nicht von dem Parameterpack abhängt. Wie zB '[=] (Zeichenkette n, int i) {return make_shared (n, args() ..., i);}' und Anpassung 'func', um zu passen. – TrentP

+0

@TrentP Nun, Antworten basieren ausschließlich auf Fragen. Ich kann nicht wissen, was das eigentliche Problem ist, also kann ich nicht sagen, was die beste Lösung dafür ist. Es tut mir Leid. – skypjack

Verwandte Themen