2012-03-30 15 views
7

Sto cercando di capire il concetto di currying e di chiamare una funzione che concatena tre stringhe ma passando solo due stringhe e usando il secondo argomento due volte.Funzione C++ associare argomenti ripetuti alla funzione al curry

Tuttavia, quando lo faccio, il secondo argomento non viene inviato alla funzione e stampa una stringa vuota. È un errore davvero ovvio?

string concatthreestrings(string a,string b,string c){ 
    cout<<"Value of A: "<<a<<endl; 
    cout<<"Value of B: "<<b<<endl; 
    cout<<"Value of C: "<<c<<endl; 
    return a+b+c; 
} 


int main() 
{ 
    typedef std::function< string(string,string) > fun_t ; 
    using namespace std::placeholders; 
    fun_t fn = std::bind(concatthreestrings, _1, _2, _2); 
    cout<<endl<<fn("First","Second")<<endl; 

} 

Questo sta dando l'output di seguito. Se non usa il _2 due volte significa che il secondo argomento deve essere passato sia per il secondo che per il terzo. Se si usa una stringa al suo posto funziona bene.

enter image description here

+0

Domanda molto interessante, non mi sarei mai aspettato un simile comportamento! –

+1

Solo per aggiungere qualche correzione: questo non è davvero un curriculum. Argomenti vincolanti e curring sono due operazioni molto simili ma comunque distinte, che non devono essere confuse. Currying significa assumere una funzione che prende una funzione di N argomenti e trasformarla in una funzione di un argomento che restituisce una funzione di un argomento che restituisce una funzione di un argomento ... (ripetuto n volte). Puoi usare 'std :: bind' per implementare una funzione' curry' che fa questo per te (in qualche modo). Allo stesso modo è possibile utilizzare il currying per implementare l'associazione degli argomenti nel modo di 'std :: bind'. – LiKao

+1

@LiKao: Infatti, 'bind' consente [applicazione parziale] (http://en.wikipedia.org/wiki/Partial_application), non in fase di elaborazione. –

risposta

5

stringhe copia è costoso. Dal momento che std::bind pensa che i valori dei segnaposto vengano utilizzati una sola volta, esegue uno std::move sulle stringhe. Questo viene fatto per ogni parametro e, di conseguenza, b o c è uno spostamento, ovvero stringa vuota.

È possibile modificare questo comportamento dicendo esplicitamente quello che vuoi dire, passando gli argomenti con const riferimento:

string concatthreestrings(string const& a,string const& b,string const& c) 

Ora, dovrebbe funzionare.

+2

Sei sicuro che questo comportamento sia ben definito? Da quanto ho letto nello standard, il wrapper restituito da 'bind' inoltra i suoi argomenti alla funzione wrapping. In questo caso, secondo la mia comprensione, avremmo un wrapper in questo modo (dopo aver dedotto i parametri del template e collassando): 'string g (const char (& u1) [6], const char (& u2) [7]) { concatthreestrings (forward (u1), forward (u2), forward (u2)); } '. Le tre stringhe 'a',' b' e 'c' sarebbero costruite dagli array, quindi non vedo dove si sarebbe verificata la mossa. –

+0

@ipc: Esiste un modo per specificare questo comportamento in std :: bind piuttosto che modificare la definizione della funzione stessa – ganeshran

+0

L'accesso all'oggetto spostato è UB. –

2

ho fatto alcuni test utilizzando questo esempio più piccolo che presenta lo stesso comportamento si ha:

#include <functional> 
#include <iostream> 
#include <string> 

using std::string; 

void print(string s1, string s2) 
{ 
    std::cout << s1 << s2 << '\n'; 
} 

int main() 
{ 
    using namespace std::placeholders; 

    typedef std::function< void(string) > fn_t; 

    fn_t func = std::bind(print, _1, _1); 

    std::string foo("foo"); 
    func(foo); 
} 

// outputs: foo 

Si noti che ho definito un oggetto stringa denominata "pippo" invece di utilizzare stringhe letterali. Il comportamento è lo stesso, quindi il problema non è correlato a questo.

Penso che il problema derivi dal typedef. Il ritorno di bind (che non è specificato) è assegnato a una funzione prendendo un stringdal valore, mentre il wrapper restituito dal binding probabilmente prende i suoi argomenti in base al riferimento-valore e li inoltra perfettamente. Invece di utilizzare il proprio typedef, è necessario utilizzare la parola chiave auto, in modo che il tipo di func venga dedotto automaticamente dal compilatore. Se modifichiamo il principale come segue, si ottiene il comportamento previsto:

int main() 
{ 
    using namespace std::placeholders; 

    auto func = std::bind(print, _1, _1); 

    std::string foo("foo"); 
    func(foo); 
} 

// outputs: foofoo 

Un'altra soluzione è quella di sostituire il vostro typedef in modo che func prende il parametro per riferimento-a-const:

typedef std::function< void(string const &) > fn_t; 

I don capisco davvero perché l'altro typedef non funzioni ... Presumibilmente la stringa viene spostata, come notato da @ipc, ma non so a che punto dell'esecuzione ciò avvenga. Non sono nemmeno sicuro che si tratti di un comportamento standard, dal momento che siache il wrapper restituito da bind devono utilizzare l'inoltro perfetto. Forse GCC include alcune ottimizzazioni che spostano gli argomenti del wrapper quando vengono passati per valore?

Modifica

ho fatto alcuni test, si scopre l'implementazione di GCC di std::function esegue una mossa suoi argomenti, mentre il ritorno da involucro std::bind non lo fa. Non so ancora se questo è standard, ho intenzione di scrivere una domanda al riguardo.

+0

ya Penso che sia un problema di implementazione del compilatore dal momento che VC non lo mostra. – ganeshran

Problemi correlati