3

Why is B::operator() called for both B and D in the program below?

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

If I change std::sort(v.begin(), v.end(), b) to this:

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

then f(d, vd) sorts backwards as expected.

My best understanding is that std::sort(v.begin(), v.end(), b) uses &B::operator() rather than b.operator(). I couldn't find a clear explanation though, and it doesn't seem entirely logical since the compiler should be able to see that B has a vtable.

AdSR
  • 625
  • 1
  • 6
  • 7
  • 3
    `std::sort` takes the comparator by value. The real lesson here is that non-leaf base classes should be abstract, which would avoid precisely this kind of misunderstanding. – Kerrek SB Jul 08 '16 at 09:08

1 Answers1

4

See the signature of std::sort:

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

The parameter comp will be passed by value here, so for f(d, vd);, d will be slicing copied to B and then B::operator() will be invoked.

You might make f() a template function.

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

Other suggestion: It'll be better to make B::operator() and D::operator() const member function, and change the parameter type of b to const reference.

LIVE

Community
  • 1
  • 1
songyuanyao
  • 169,198
  • 16
  • 310
  • 405