Search code examples
c++operator-overloadingswap

STL sort a vector by customer '<' operator. Why should I define the '<' operator as const?


I have defined a class "HasPtr":

#include <iostream>
#include <string>

class HasPtr {
public:
   HasPtr()
      : ps(new std::string()), i() {
      std::cout << "Default constructor execute" << std::endl;
   }
   HasPtr(const std::string &s, int a)
      : ps(new std::string(s)), i(a) {
      std::cout << "Sting with int constructor execute" << std::endl;
   }
   HasPtr(const std::string &s = std::string())
      : ps(new std::string(s)), i(std::stoi(s)) {
      std::cout << "String constructor execute" << std::endl;
   }
   HasPtr(const HasPtr &obj)
      : ps(new std::string(*obj.ps)), i(obj.i) {
      std::cout << "Copy constructor execute" << std::endl;
   }
   HasPtr & operator=(const HasPtr &rhs) {
      std::cout << "Assign execute" << std::endl;
      ps = new std::string(*rhs.ps);
      i = rhs.i;
      return *this;
   }
   ~HasPtr() {
      delete ps;
   }

   std::string get_str() const {
      return *ps;
   }

   int get_i() const {
      return i;
   }

   bool operator<(const HasPtr obj) const {
      std::cout << "Operator < execute" << std::endl;
      return i < obj.i;
   }

   friend void swap(HasPtr &lhs, HasPtr &rhs) {
      std::cout << "HasPtr::swap function execute" << std::endl;
      std::swap(lhs.ps, rhs.ps);
      std::swap(lhs.i, rhs.i);
   }

private:
   std::string *ps;
   int i;
};

This is my main.cpp:

#include <iostream>
#include <vector>
#include <algorithm>
#include "HasPtr.h"

int main() {
   std::vector<HasPtr> v;
   v.push_back(std::to_string(10));
   v.push_back(std::to_string(5));
   v.push_back(std::to_string(7));
   v.push_back(std::to_string(3));
   v.push_back(std::to_string(2));
   v.push_back(std::to_string(9));

   std::cout << "==========List the elements==========" << std::endl;
   for (const auto &i : v) {
      std::cout << i.get_str() << " ";
   }
   std::cout << std::endl;
   std::cout << "=====================================" << std::endl;

   sort(v.begin(), v.end());

   std::cout << "==========List the elements==========" << std::endl;
   for (const auto &i : v) {
      std::cout << i.get_str() << " ";
   }
   std::cout << std::endl;
   std::cout << "=====================================" << std::endl;
}
  1. I want to know why should I define the bool operator<(const HasPtr obj) const as const? I think all elements in the vector aren't const. Right?
  2. I don't understand the execution:
String constructor execute
Copy constructor execute
String constructor execute
Copy constructor execute
Copy constructor execute
String constructor execute
Copy constructor execute
Copy constructor execute
Copy constructor execute
String constructor execute
Copy constructor execute
Copy constructor execute
Copy constructor execute
Copy constructor execute
String constructor execute
Copy constructor execute
Copy constructor execute
Copy constructor execute
Copy constructor execute
Copy constructor execute
String constructor execute
Copy constructor execute
==========List the elements==========
10 5 7 3 2 9
=====================================
Copy constructor execute
Copy constructor execute
Operator < execute
Copy constructor execute
Operator < execute
Assign execute
Assign execute
Copy constructor execute
Copy constructor execute
Operator < execute
Copy constructor execute
Operator < execute
Copy constructor execute
Operator < execute
Assign execute
Copy constructor execute
Operator < execute
Assign execute
Copy constructor execute
Copy constructor execute
Operator < execute
Copy constructor execute
Operator < execute
Assign execute
Assign execute
Assign execute
Assign execute
Copy constructor execute
Copy constructor execute
Operator < execute
Copy constructor execute
Operator < execute
Assign execute
Assign execute
Assign execute
Assign execute
Assign execute
Copy constructor execute
Copy constructor execute
Operator < execute
Copy constructor execute
Operator < execute
Copy constructor execute
Operator < execute
Assign execute
Copy constructor execute
Operator < execute
Assign execute
==========List the elements==========
2 3 5 7 9 10
=====================================

Why there are so many "copy construction" and "assignment"?

  1. Why the swap function has not been called? How the vector rearrange the elements?

I have been told that when the vector's element number is small, it uses a different algorithm but not swap.

The default constructor HasPtr() and the String constructor HasPtr(const std::string &s = std::string()) are not ambiguous. I don't know why.

Thanks


Solution

  • About the const qualifier of operator<():

    Imagine you have this function

    int
    my_fnct(const HasPtr &a,
            const HasPtr &b)
    {
      int result=12;
      // ... do something ...
      if(a<b) // <--- the comparison is important
      {
        result+=100; // or whatever ...
      }
      // ... do something ...
      return result;
    }
    

    If your HasPtr::operator<() was declared as

    bool operator<(const HasPtr &obj) // <-- without const here
    { /* ... */ }
    

    then the call a<b in the previous my_fnct() would not be allowed by the compiler since the parameter a is declared as const but the operator<() does not promise in its prototype that a will not be modified..

    On the other hand, if your HasPtr::operator<() is declared as

    bool operator<(const HasPtr &obj) const // <-- const at the end here
    { /* ... */ }
    

    then the call a<b in the previous my_fnct() will be allowed by the compiler since the const keyword at the end of the prototype ensures that the left operand of the comparison (a in my previous example) will not be modified.