2016-02-03 10 views
16

Betrachten Sie diesen Code:C++ Member-Funktion Pointer Definition

#include <iostream> 
#include <functional> 

struct B { 
    template <class C, class M, class T> 
    void call1(C (M::*member)(), T *instance) { 
     std::function<void()> fp = std::bind(member, instance); 
     fp(); 
    } 

    template <class C, class M, class T> 
    void call2(C (M::*member), T *instance) { 
     std::function<void()> fp = std::bind(member, instance); 
     fp(); 
    } 

    void foo() { 
     call1(&B::func, this); // works 
     call2(&B::func, this); // works 

     call1(&B::func2, this); // Error: no matching member function for call to 'call2' 
     call2(&B::func2, this); // works 
    } 

    void func() { 
     std::cout << "func\n"; 
    } 

    void func2() const volatile { 
     std::cout << "func2\n"; 
    } 
}; 

int main() { 
    B{}.foo(); 
} 

Es scheint, dass die neuere Version nicht akzeptieren Funktionen mit extra cv-Qualifikation.

Was ist der Unterschied zwischen der Angabe eines Funktionsmembers wie dieser C (M::*member)() und so C (M::*member)?

+0

Ich wusste nicht einmal 'C (M :: * member)' ist gültige Syntax ... –

+0

Der Punkt ist, dass 'call1' keine Funktionen mit cv-Qualifier akzeptieren, während' call2' das erlaubt. – Crossfire

+1

Unzusammenhängend, aber warum sollte man 'std :: bind' verwenden, anstatt nur die Elementfunktion direkt aufzurufen? '(instance -> * member)();' –

Antwort

15

ist nur Lassen vereinfachen, um den Unterschied zwischen Berücksichtigung

template <class C, class M> void f(C (M::*member)()); 
template <class C, class M> void g(C (M::*member)); 

In f ist member ein Zeiger auf eine Memberfunktion von M Rückkehr Null Argumente nehmen und C zurück. Wenn Sie es mit &B::func aufgerufen haben, wird der Compiler M == B und C == void ableiten. Einfach.

In g, member ist nur ein Zeiger auf ein Mitglied M des Typs C. Aber in unserem Fall ist &B::func eine Funktion. Die Auswirkung hier ist also nur den Zeiger fallen zu lassen. Wir leiten wieder M == B ab, während C wird void() - jetzt C ist ein Funktionstyp. Dies ist eine weniger spezialisierte Version von f in dem es für mehr Arten von Mitgliedern ermöglicht. g kann mit Funktionen übereinstimmen, die Argumente entgegennehmen, oder gegen Zeiger auf Mitglieder oder, relevant, gegen cv -qualifizierte Mitglied functinos.

Lassen Sie uns als Beispiel eine überladene Funktion betrachten und wie wäre es anders ableiten lassen (dies war das ursprüngliche Problem in Ihrer Frage, die bearbeitet seit wurde, ist aber immer noch interessant):

struct X { 
    void bar() { } 
    void bar(int) { } 
}; 

Wenn wir tun:

f(&X::bar); 

obwohl &X::bar ist ein überlasteter Name, nur ein Spiel tatsächlich C (M::*)(). Derjenige, der M == X und C == void hat. Es gibt einfach keine Möglichkeit, dass die Überladung von bar unter Verwendung einer int den Vorlagentyp entspricht. Dies ist einer der zulässigen Verwendungszwecke, einen überladenen Namen zu übergeben. Das folgert Geldbuße.

Wenn wir jedoch tun:

g(&X::bar); 

Nun gibt es zwei perfekt vaild Abzüge. C könnte sowohl void() als auch void(int) sein. Da beide gültig sind, ist der Abzug mehrdeutig und Sie können nicht kompilieren - mit einem Fehler, der dies nicht wirklich deutlich macht.


Sie jetzt zum Beispiel zurück:

call1(&B::func2, this); // Error: no matching member function for call to 'call2' 
call2(&B::func2, this); // works 

Die Art der &B::func2 ist void (B::*)() const volatile. Da call1 auf einen Member-Funktionstyp ableitet, der keine Argumente akzeptiert und nicht cv-qualifiziert ist, schlägt der Typabzug einfach fehl.Es gibt keine C oder M, um diese Typen zu finden.

Allerdings ist der call2 Abzug in Ordnung, da es generischer ist. Es kann jeder Zeiger auf Mitglied entsprechen. Wir enden einfach mit C = void() const volatile. Deshalb funktioniert das.

+0

Gibt es eine Möglichkeit, einen Funktionszeiger zu erstellen, der Parameterüberladungen akzeptieren kann, aber cv-Qualifier ignoriert? Das heißt, die verallgemeinerte Version 'C (M :: * member)' akzeptiert Argumente und ignoriert cv-Qualifier. Wenn ich Argumente wie 'C (M :: * member) (A1, A2)' festlege, muss ich diese Aufruferfunktionen mit 'C (M :: * member) (A1, A2) const 'und' C (M :: * member) (A1, A2) flüchtig. – Crossfire

+1

@Crossfire Machen Sie einfach ein Typmerkmal wie ... 'template struct is_func_const: std :: false_type {}; Vorlage Struktur is_func_const : std :: true_type {}; Vorlage Struktur is_func_const : std :: true_type {}; 'und dann können Sie SFINAE verwenden. – Barry