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?
CodePudding user response:
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>&