2011-09-28 7 views
100

Dla jednej klasy chcę przechowywać niektóre wskaźniki funkcji dla funkcji składowych tej samej klasy w jednym obiekcie map przechowującym obiekty std::function. Ale nie na samym początku z tym kodem:Używanie ogólnych obiektów std :: function z funkcjami składowymi w jednej klasie

class Foo { 
    public: 
     void doSomething() {} 
     void bindFunction() { 
      // ERROR 
      std::function<void(void)> f = &Foo::doSomething; 
     } 
}; 

otrzymam error C2064: term does not evaluate to a function taking 0 arguments w xxcallobj połączeniu z niektórych dziwnych błędów konkretyzacji szablonu. Obecnie pracuję na Windows 8 z Visual Studio 2010/2011 i na Win 7 z VS10 również się nie udaje. Błąd musi być oparty na dziwnych zasadach C++, których nie przestrzegam.

EDYCJA: Robię NOT użyj zwiększenia. To jest C++ 11 zintegrowany z kompilatorem MS.

+0

Poprawną odpowiedzią jest użycie 'std :: invoke', które nie jest obsługiwane przez MSVC2013. To pytanie jest trochę przestarzałe. – Mikhail

Odpowiedz

180

Non-statyczna funkcja członek musi być wywołanym z obiektem. Oznacza to, że zawsze domyślnie przekazuje "ten" wskaźnik jako swój argument.

Ponieważ podpis std::function określa, że ​​funkcja nie przyjmuje żadnych argumentów (<void(void)>), należy wiążą pierwszy (i jedyny) argument.

std::function<void(void)> f = std::bind(&Foo::doSomething, this); 

Jeśli chcesz powiązać funkcję z parametrami, trzeba określić zastępcze:

using namespace std::placeholders; 
std::function<void(int,int)> f = std::bind(&Foo::doSomethingArgs, this, _1, _2); 

Albo, jeśli Twój kompilator obsługuje C++ 11 lambdy:

std::function<void(int,int)> f = [=](int a, int b) { 
    this->doSomethingArgs(a, b); 
} 

(Nie mam kompilatora obsługującego C++ 11 pod ręką teraz, więc nie mogę sprawdzić tego.)

+1

Ponieważ nie jestem zależny od boost, użyję wyrażeń lambda;) Niemniej jednak dziękuję! –

+3

@AlexB: Boost.Bind nie używa ADL dla symboli zastępczych, umieszcza je w anonimowym obszarze nazw. – ildjarn

+11

Zalecam unikanie globalnego przechwytywania [=] i użycie [this], aby było bardziej zrozumiałe, co zostało przechwycone (Scott Meyers - Effective Modern C++ Rozdział 6. element 31 - Unikanie domyślnych trybów przechwytywania) –

53

Albo trzeba

std::function<void(Foo*)> f = &Foo::doSomething; 

tak, że można nazwać go na dowolnej instancji, albo trzeba powiązać konkretny przypadek, na przykład this

std::function<void(void)> f = std::bind(&Foo::doSomething, this); 
3

Jeśli trzeba przechowywać funkcji składowej bez instancji klasy, można zrobić coś takiego:

class MyClass 
{ 
public: 
    void MemberFunc(int value) 
    { 
     //do something 
    } 
}; 

// Store member function binding 
auto callable = std::mem_fn(&MyClass::MemberFunc); 

// Call with late supplied 'this' 
MyClass myInst; 
callable(&myInst, 123); 

Co by wygląd rodzaj magazynowania jak bez auto? coś takiego:

std::_Mem_fn_wrap<void,void (__cdecl TestA::*)(int),TestA,int> callable 

Można również przekazać tę funkcję pamięci masowej do standardowej funkcji wiążącej

std::function<void(int)> binding = std::bind(callable, &testA, std::placeholders::_1); 
binding(123); // Call 

przeszłość i przyszłość Uwagi: Starsze interfejsu std :: mem_func istniał, ale od tego czasu został przestarzały. Istnieje propozycja, post C++ 17, aby pointer to member functions callable. Byłoby to najbardziej pożądane.

+0

@Danh 'std :: mem_fn' was ** nie ** usunięte; wiązało się z niepotrzebnymi przeciążeniami. Z drugiej strony 'std :: mem_fun' został przestarzały w C++ 11 i zostanie usunięty przy pomocy C++ 17. –

+0

@ Danh Dokładnie o tym mówię;) Pierwsze "podstawowe" przeciążenie wciąż istnieje: 'szablon nieokreślony mem_fn (RT :: *);', i to nie pójdzie z dala. –

+0

@Danh Przeczytaj uważnie [DR] (https://cplusplus.github.io/LWG/lwg-defects.html#2048). 12 z 13 przeciążeń zostało usuniętych przez DR. Ten ostatni * nie był * (i nie będzie, ani w C++ 11 ani C++ 14). –

Powiązane problemy