2016-07-08 7 views
3

Warum wird B::operator() sowohl für B als auch im unten stehenden Programm aufgerufen?Warum wird der Operator aus der Basisklasse in Templates aufgerufen?

#include <algorithm> 
#include <iostream> 
#include <iterator> 
#include <vector> 

class B { 
public: 
    virtual ~B() {} 
    virtual bool operator()(int a, int b) { return a < b; } 
}; 

class D : public B { 
public: 
    bool operator()(int a, int b) override { return a > b; } 
}; 

void f(B& b, std::vector<int>& v) 
{ 
    std::sort(v.begin(), v.end(), b); 
    std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); 
    std::cout << std::endl; 
} 

int main() 
{ 
    const std::vector<int> v{5, 3, 8, 1, 7}; 

    std::vector<int> vb(v); 
    B b; 
    f(b, vb); 

    std::vector<int> vd(v); 
    D d; 
    f(d, vd); 

    return 0; 
} 

Wenn ich ändern std::sort(v.begin(), v.end(), b) dazu:

std::sort(v.begin(), v.end(), 
      [&](int x, int y){ return b.operator()(x, y); }); 

dann f(d, vd) sortiert nach hinten, wie erwartet.

Mein bestes Verständnis ist, dass std::sort(v.begin(), v.end(), b)&B::operator() statt b.operator() verwendet. Ich konnte jedoch keine klare Erklärung finden, und es scheint nicht völlig logisch, da der Compiler in der Lage sein sollte zu sehen, dass B eine Vtable hat.

+3

'std :: sort' nimmt den Komparator von Wert. Die wirkliche Lektion hier ist, dass Nicht-Blatt-Basisklassen abstrakt sein sollten, was genau diese Art von Missverständnissen vermeiden würde. –

Antwort

4

Siehe die Unterschrift von std::sort:

template< class RandomIt, class Compare > 
void sort(RandomIt first, RandomIt last, Compare comp); 

Der Parameter comp wird hier als Wert übergeben werden, so dass für f(d, vd);, dslicing copied-B und dann wird B::operator() wird aufgerufen werden.

Sie könnten f() eine Vorlagenfunktion machen.

template <typename COMP> 
void f(COMP& b, std::vector<int>& v) 
{ 
    std::sort(v.begin(), v.end(), b); 
    std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); 
    std::cout << std::endl; 
} 

Andere Vorschlag: Es wird besser sein B::operator() und D::operator() const Member-Funktion zu machen, und ändern Sie den Parametertyp b auf konstante Referenz.

LIVE

+0

Zusammen mit dem (etwas außerthematischen aber wertvollen) const Korrektheitshinweis für die 'operator()' Elementfunktionen; möglicherweise lohnt sich zu addieren, um auch das Argument "b" in "f" eine konstante Referenz zu sein. – dfri

+0

@dfri Sicher, hinzugefügt. Tatsächlich habe ich es in meiner Live-Demo gemacht. :) – songyuanyao

+0

Ah, ich habe die Live-Demo nicht überprüft :) – dfri

Verwandte Themen