2016-07-08 8 views
3

Почему B::operator() вызывал как B, так и D в программе ниже?Почему оператор из базового класса называется в шаблонах?

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

Если изменить std::sort(v.begin(), v.end(), b) к этому:

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

затем f(d, vd) сорта назад, как и ожидалось.

Мое лучшее понимание заключается в том, что std::sort(v.begin(), v.end(), b) использует &B::operator(), а не b.operator(). Я не мог найти четкого объяснения, хотя, и это не кажется полностью логичным, поскольку компилятор должен уметь видеть, что B имеет vtable.

+3

'станд :: sort' принимает компаратор по значению. Настоящий урок здесь состоит в том, что базовые классы, не связанные с листами, должны быть абстрактными, что позволило бы избежать такого рода недоразумений. –

ответ

4

Смотреть подпись std::sort:

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

Параметр comp будет передаваться по значению здесь, так что для f(d, vd);, d будет slicing copied к B, а затем B::operator() будет вызван.

Вы можете сделать f() функцию шаблона.

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

Другое предложение: Это будет лучше сделать B::operator() и D::operator() константной функцией члена, и изменить тип параметра b к константной ссылке.

LIVE

+0

Наряду с (немного неточным, но ценным) советом по корректности для функций 'operator()' member; возможно, стоит добавить также, чтобы аргумент 'b' в' f' был константной ссылкой. – dfri

+0

@dfri Конечно, добавлено. На самом деле я сделал это в своей живой демоверсии. :) – songyuanyao

+0

А, я не проверял демо-версию! – dfri

 Смежные вопросы

  • Нет связанных вопросов^_^