2015-04-13 10 views
12

la seguente:std :: bind non ha senso per me qualunque

#include <functional> 

struct Foo 
{ 
    void bar1() {} 
    void bar2(int) {} 
    void bar3(int, int) {} 
    void bar4(int, int, int) {} 
}; 


int main() 
{ 
    Foo foo; 

    auto f1 = std::bind(&Foo::bar1, &foo); 
    auto f2 = std::bind(&Foo::bar2, &foo); 
    auto f3 = std::bind(&Foo::bar3, &foo); 
    auto f4 = std::bind(&Foo::bar4, &foo); 

    f1(1, 2, 3, 4); // success 
    f2(1, 2, 3); // error 
    f3(1, 2);  // error 
    f4(1);   // error  

    return 0; 
} 

f1 (1, 2, 3, 4) compila ed esegue bar1(). f2 (1, 2, 3), non viene compilato, f3 (1, 2) non viene compilato (tuttavia bar3 ha il prototipo corretto) e f4 (1) non viene compilato. L'errore che ottengo con Visual Studio 2013 per questi 3 casi è

"la classe non definisce un 'operatore()' o un operatore di conversione definito dall'utente su un puntatore a funzione o un riferimento a funzione che richiede numero appropriato di argomenti "

Ho una comprensione limitata dei modelli e della libreria standard, ma questo non sembra avere alcun senso logico per me. C'è una spiegazione ragionevole semplice?

+0

La mia soluzione personale per 'std :: bind': è C++ 11 e tu hai lambda, non pensarci più. 'auto f1 = [&]() {pippo.bar1()};' –

+9

Probabilmente hai dimenticato di mettere i segnaposto (_1, _2, _3) in 'f2',' f3', 'f4'. @Matteo Italia 'bind' è a volte più conciso di un lambda. – sbabbi

+3

OK, quindi f1 (1, 2, 3, 4) che si lega a bar1() non ha bisogno di alcun segnaposto. Ma poiché operator() non "controlla" arity, viene chiamato bar1() e (1, 2, 3, 4) viene scartato dal template magic, senza errori. – Robinson

risposta

11

Per passare argomenti alla destinazione è necessario fornire tali argomenti nell'espressione di bind o lasciarli non associati aggiungendo segnaposti all'espressione di bind e quindi è necessario chiamare la funzione con argomenti per sostituire i segnaposti.

È possibile chiamare bar1 senza segnaposto perché non accetta alcun argomento, quindi non è necessario passare nulla ad esso. Gli argomenti che passi a f1 vengono semplicemente ignorati, perché non ci sono argomenti non associati, cioè nessun segnaposto che deve essere sostituito.

Le altre funzioni richiedono argomenti, quindi è necessario associare gli argomenti al "bind time", ad es.

auto f2a = std::bind(&Foo::bar2, &foo, 1); 
f2a(); 

o lasciare gli argomenti non legato e di fornire loro quando si richiama l'oggetto callable:

auto f2b = std::bind(&Foo::bar2, &foo, std::placeholders::_1); 
f2b(1); 

noti che GCC 5 ha ora affermazioni statici per la cattura di questo tipo di errore. Se l'arità della funzione obiettivo può essere determinata e l'espressione di bind non ha né un argomento associato né un segnaposto per ogni parametro della funzione obiettivo, allora dice:

/usr/local/gcc-head/include/C++/5.0.0/funzionale: 1426: 7: errore: asserzione static non riuscita: il numero errato di argomenti per puntatore-a-membro

Quello che hai scritto è equivalente a questo, utilizzando lambda invece di bind:

Foo foo; 

auto f1 = [&foo](...) { return foo.bar1(); }; 
auto f2 = [&foo](...) { return foo.bar2(); }; 
auto f3 = [&foo](...) { return foo.bar3(); }; 
auto f4 = [&foo](...) { return foo.bar4(); }; 

f1(1, 2, 3, 4); // success 
f2(1, 2, 3); // error 
f3(1, 2);  // error 
f4(1);   // error 

ie si definiscono i funtori che accettano qualsiasi argomento, ma li ignorano e quindi chiamano le funzioni membro di Foo, ma non necessariamente con gli argomenti giusti.

+0

Grazie Jonathan. – Robinson

4

Sembra che tu abbia un fraintendimento di ciò che dovrebbe fare bind.
Gli argomenti passati al risultato di bind sono "sostituiti" nei segnaposti _1, _2 e così via. Gli argomenti che non corrispondono a un segnaposto sono, beh, inutilizzati e quindi ignorati. Poiché non utilizzi alcun segnaposto nelle tue espressioni di bind, la chiamata interna a bar1 equivale sempre a foo.bar1(), quindi è indipendente dagli argomenti passati al risultato di bind.

Ora diventa chiaro che la chiamata a bar4 equivale a foo.bar4(), ma bar4 prevede quattro argomenti, quindi non ha senso. Si può rimediare scrivendo

using namespace std::placeholders; 
auto f4 = std::bind(&Foo::bar4, &foo, _1, _2, _3, _4); 

Ora, se si fornisce quattro argomenti, quelli saranno trasmessi correttamente e la chiamata interna diventa equivalente ad esempio foo.bar4(1, 2, 3, 4).