2015-02-06 11 views
5

Sono relativamente nuovo per C++ e questo è il mio primo post, in modo da essere gentile ;-)Passando funtore dinamica per stl

capisco come passare puntatori a funzione o oggetti funzione, come molte funzioni STL richiedono. Non mi è chiaro come utilizzarlo in combinazione con l'ereditarietà.

In particolare mi piacerebbe chiamare una funzione stl con diversi funtori che ereditano da un funtore di base. Non ho trovato commenti o best practice rilevanti su questo argomento. Ecco un frammento di codice semplificato:

struct generator{ 
    virtual int operator()() = 0; 
    virtual ~generator(){}; 
}; 

struct A : public generator{ 
    int operator()(); 
}; 

struct B : public generator{ 
    int operator()(); 
}; 

int main() 
{ 
    auto v = std::vector<std::array<int, 500>>(2); 
    std::array<std::shared_ptr<generator>, 2> functors = {{std::make_shared<A>(), 
                 std::make_shared<B>()}}; 
    for (size_t i = 0; i < functors.size(); ++i) 
    std::generate(std::begin(v[i]), std::end(v[i]), 
        *(functors[i])); 
    return 0; 
} 

Compilazione questo dà:

clang++ -std=c++11 -g -Wall -Wextra test_stackoverflow.cpp 
test_stackoverflow.cpp:25:5: error: no matching function for call to 'generate' 
    std::generate(std::begin(v[i]), std::end(v[i]),*(functors[i])); 
    ^~~~~~~~~~~~~ 
/usr/lib/gcc/i686-linux-gnu/4.6/../../../../include/c++/4.6/bits/stl_algo.h:5003:5: note: candidate template ignored: substitution 
     failure [with _ForwardIterator = int *, _Generator = generator]: parameter type 'generator' is an abstract class 
    generate(_ForwardIterator __first, _ForwardIterator __last, 

Sì, il tipo di * (funtori [i]) è generatore. Che non consente l'accesso dinamico a A :: operator() e B :: operator(). Capisco come (in modo rudimentale) risolvere questo. per esempio.

std::generate(std::begin(v[i]), std::end(v[i], 
       [&]{ return functors[i]->operator()(); }; 

Tuttavia, questo sembra piuttosto offuscato e sto cercando un modo più chiaro per raggiungere i miei obiettivi.

Come si passano di solito tali funzioni dinamiche. O se questo è disapprovato, qual è la migliore alternativa?

Qualsiasi aiuto o suggerimento sono i benvenuti.

risposta

1

Il problema, come forse già sapete, è che std::generate prende il generatore in base al valore. Per passare un oggetto polimorfico, devi passare per riferimento.

E c'è una soluzione esattamente per questo tipo di problema in C++ 11 (oltre all'opzione lambda, che è ancora più elegante di quella che avevamo prima di C++ 11, credo). La soluzione è std::ref. Restituisce un wrapper di riferimento che può essere passato per valore.

Questo dovrebbe funzionare per voi:

std::generate(std::begin(v[i]), std::end(v[i]), 
       std::ref(*(functors[i]))); 

Il motivo per cui si può passare wrapper di riferimento come un funtore, è che attuano il operator()

+0

Inoltre, 'std :: function' –

+0

Confermato, può passare 'std :: ref' agli algoritmi. Non lo sapevo. http://coliru.stacked-crooked.com/a/db815184c44240ad –

+0

Sarò onesto. Non avevo mai sentito parlare di std :: ref. Quindi grazie. Sei dell'opinione che su questo debba preferirsi una lambda, e se è così, perché? – APevar

Problemi correlati