2016-05-16 5 views
5

Sono confuso sul motivo per cui è necessario std::mem_fn.Perché usare mem_fn?

Ho una funzione che accetta qualsiasi chiamabile (lambda, puntatore di funzione, ecc.) E la lega a un argomento.

Esempio:

template<class T> 
void Class::DoBinding(T callable) { 
    m_callable = std::bind(callable, _1, 4); 
} 
//somewhere else 
Item item; 
m_callable(item); 

Tutti gli esempi di codice che ho visto fare:

//some defined member function 
Item::Foo(int n); 

DoBinding(std::mem_fn(&Item::Foo)); 

Perché non può essere semplicemente:

DoBinding(&Item::Foo); 

Sembra quest'ultimo è callable senza dover usare std :: mem_fn, quindi perché è necessario?

+1

No, non è necessario 'mem_fn' per quello. Ne hai bisogno per altre cose. –

+1

Non si dovrebbe mai passare a 'std :: bind' un valore di un tipo sconosciuto. Se il tipo è esso stesso un'espressione di binding, il comportamento è sorprendente. In altre parole, 'std :: bind' dovrebbe essere usato nel codice foglia, non nel codice generico. –

+0

E riguardo l'uso di una lambda invece di std :: bind? Sento che la sintassi è più leggibile, e quando si usa C++ 14 dovrebbe coprire tutti (quasi tutti?) Gli stessi casi d'uso associati a – AndyG

risposta

8

Questo in genere viene eseguito poiché la persona che scrive DoBinding(std::mem_fn(&Item::Foo)) non ha idea che DoBinding possa prendere direttamente un puntatore membro.

Ricordare: std::sort(..., &Item::Foo) sarà falliscono, perché sort aspetta che il valore sia un oggetto funzione direttamente richiamabili. E i puntatori membri non sono. Infatti, praticamente ogni algoritmo nella libreria standard C++ fallisce quando viene fornito un puntatore membro invece di un tipo richiamabile direttamente. Il tuo DoBinding funziona solo perché stai usando std::bind, che ha un sovraccarico speciale per i puntatori membri. Il chiamante di DoBinding non sa necessariamente che lo stai facendo.

La maggior parte del codice che accetta callables in base ai parametri del modello si strozzerà su un puntatore membro. Quindi, per essere sicuri, non passiamo i puntatori degli altri membri come oggetti che possono essere chiamati direttamente; utilizzare mem_fn per trasformarlo in tale oggetto.

10

Questo perché il codice generico che si aspetta UnaryFunction o BinaryFunction lo invocherà direttamente con la sintassi di chiamata normale. Quindi, per scegliere un algoritmo arbitrario come for_each, potrebbe benissimo essere implementato come:

template<class InputIt, class UnaryFunction> 
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f) 
{ 
    for (; first != last; ++first) { 
     f(*first); // <== N.B. f(*first) 
    } 
    return f; 
} 

Se avete chiamato for_each() con &Item::Foo, il codice tenta di chiamare (&Item::Foo)(x), che è mal formata dal momento che per i puntatori ai membri si devono scrivere (x.*&Item::Foo)(). È questa la differenza sintattica che mem_fn intende risolvere: mem_fn si occupa della sintassi di invocazione dei puntatori ai membri in modo che sia possibile utilizzare tutti gli algoritmi con puntatori ai membri nonché funzioni e oggetti funzione. Non puoi avere for_each(v.begin(), v.end(), &Item::Foo) ma puoi avere for_each(v.begin(), v.end(), mem_fn(&Item::Foo)).

Questo funziona perfettamente in std::bind() (e std::thread e std::function e ...) in modo nativo poiché tutti hanno una gestione esplicita per i puntatori ai membri separatamente. E poiché lo stesso DoBinding() chiama std::bind(), non c'è motivo per std::mem_fnin questo caso.


C'è una proposta per sbarazzarsi di questa differenza sintattica: P0312.