2015-02-06 10 views
5

Ich bin relativ neu in C++ und das ist mein erster Beitrag, so sanft seine ;-)Passing dynamischen Funktors zu stl

Ich verstehe, wie Funktionszeiger oder Funktionsobjekte zu übergeben, wie viele stl Funktionen erfordern. Es ist mir unklar, wie man dies in Kombination mit der Vererbung verwendet.

Speziell möchte ich eine STL-Funktion mit mehreren Funktoren aufrufen, die von einem Basis-Funktor erben. Ich habe keine relevanten Anmerkungen oder Best Practices zu diesem Thema gefunden. Hier ein vereinfachtes Codeausschnitt:

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; 
} 

Kompilieren ergibt dies:

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, 

Ja der Typ * (functors [i]) ist Generator. Dies wird keinen dynamischen Zugriff auf A :: operator() und B :: operator() erlauben. Ich verstehe, wie ich dies (rudimentär) lösen kann. z.B.

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

Aber das scheint eher verschleiert und ich bin auf der Suche nach einem klareren Weg, um meine Ziele zu erreichen.

Wie geht man solche dynamischen Funktoren normalerweise durch? Oder wenn das verpönt ist, was ist die beste Alternative?

Jede Hilfe oder Vorschläge sind sehr willkommen.

Antwort

1

Das Problem, wie Sie vielleicht bereits wissen, ist, dass std::generate den Generator nach Wert nimmt. Um ein polymorphes Objekt zu übergeben, müssen Sie als Referenz übergeben werden.

Und es gibt eine Lösung für genau diese Art von Problem in C++ 11 (neben der Lambda-Option, die noch eleganter ist als das, was wir vor C++ 11 hatten, glaube ich). Die Lösung ist std::ref. Es gibt einen Referenzwrapper zurück, der nach Wert übergeben werden kann.

Dies sollte für Sie arbeiten:

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

Der Grund, warum Sie Referenz-Wrapper als Funktors passieren kann, ist, dass sie die operator()

+0

auch implementieren, 'std :: function' –

+0

bestätigt haben, können Übergeben Sie 'std :: ref' an Algorithmen. Wusste ich nicht. http://coliru.stacked-crooked.com/a/db815184c44240ad –

+0

Ich werde ehrlich sein. Ich hatte noch nie von std :: ref gehört. Also vielen Dank dafür. Sind Sie der Meinung, dass ein Lambda diesem vorzuziehen ist, und wenn ja, warum? – APevar