Custom set comparison function in a template class

I was trying to figure out this exercise from a school exam.
They implemented an abstract template Book class, and the assignment is to implement a bookshelf class.
I tried to construct a set of book pointers with a custom comparator, but then I encounter a compilation error:

In template: reference to type 'const Book<std::basic_string<char>>' could not bind to an lvalue of type 'const std::_Rb_tree<...>

(I implemented a sub class BOOK2 just for debugging purposes)

This is the long given book abstract class

#include <iostream>
#include <set>
#include <string>
#include <utility>

template <class T>
class Book
{
  // any member variables are inaccessible to descendants
 private:
  std::string _title; // do not call a copy-ctr
  T _author; // do not call a copy-ctr
  size_t _number_of_pages;
 public:
  Book(std::string title,
       T author,
       size_t number_of_pages)
      : _title(std::move(title)),
        _author(std::move(author)),
        _number_of_pages(number_of_pages)
  {}

  virtual ~Book() = default;

  const std::string& get_title() const
  { return _title; }

  const T& get_author()  const
  { return _author; }

  size_t get_number_of_pages() const
  { return _number_of_pages; }

 public:
  virtual Book<T>* clone() const = 0; // implemented *only* by descendent classes

  virtual bool is_available_on(const std::string& platform) const = 0; // implemented *only* by descendant classes

 protected:
  virtual void do_output(std::ostream& os) const // can be overridden; can be accessed *only* by descendants
  {
    os << _title << ", " << _author << ", " << _number_of_pages << " pages";
  }

  // output should depend on who book really is
  friend std::ostream& operator<<(std::ostream& os, const Book& book)
  {
    book.do_output(os);
    return os;
  }
};

This is what I implemented:

class Book2: public Book<std::string>{
 public:
  Book2(std::string &title,
        std::string &author,
        size_t number_of_pages)
      : Book<std::string>(title,author,number_of_pages){}

  bool is_available_on(const std::string &platform) const override{return
        true;}
  Book<std::basic_string<char>> * clone() const override{
    Book<std::basic_string<char>> * a{};
    return  a;
  }
};


template<class TP>
static bool book_comp(const Book<TP>& a,const Book<TP> & b){
  return a.get_title()<b.get_title();}

template<class TT>
class Bookshelf
{
 public:
  typedef bool(*book_comp_t)(const Book<TT>& a,const Book<TT> & b);
  // DO NOT CHANGE NEXT TWO LINES:
  auto& get_books() { return _books; } // DO NO CHANGE
  auto& get_books() const { return _books; } // DO NO CHANGE

  Bookshelf():_books(book_comp<TT>){}

  void add(Book<TT>& book)
  {
    size_t init_size=_books.size();
    _books.insert (&book);
    if(init_size==_books.size()){
      throw std::invalid_argument("book already in bookshlf");
    }
  }

  // sorted lexicographically by title
  friend std::ostream& operator<<(std::ostream& os, const Bookshelf<TT>&
      bookshelf)
  {
    for(const auto& book :bookshelf._books)
    {
      os << *book << std::endl;
    }
  }

 private:
  std::set<Book<TT>*,book_comp_t> _books;
};

int main ()
{
  std::string  a ="aba";
  std::string bb ="ima;";
  Book2 b = Book2(a, bb, 30);
  Bookshelf<std::string> shelf;
  std::cout<<b;
  shelf.add(b);
}

I tried changing the const qualifiers in some places, and it didn’t work.
I also tried without using the custom comparator function which worked ok.
I think this is probably some syntax error maybe?

>Solution :

std::set<Book<TT>*,book_comp_t> _books; is a set of Book<TT>*, and thus requires a comparator whose parameters are of type Book<TT>*, not const Book<TT>&

Leave a Reply