1

I'm trying to create a functional inheritance hierarchy. We are working with implementing our own priority queue class template, and the program includes lists such as buy orders.

My idea was to have "p_queue" as the base class, and let the "orders" class inherit from it, and then let subclasses such as "buy_orders" inherit from the orders class. My reasoning is that every order in this program is a priority queue, as they will be sorted by it.

Normally I would understand the error "type 'base_class' is not a direct base of 'sub_class'" if the derived class wasn't directly inheriting the superclass. But since the orders class inherits the p_queue class and no other class directly inherits from priority queue class, I'm confused why I get this error message.

Here is the code

//main.cpp #include "orders.h" int main(){ return 0; } 
//orders.h #include "p_queue.h" template<typename T> struct less{ bool operator()(const T& a, const T& b){ return a < b; } }; template<typename T> class Orders : public p_queue<T, decltype(less<T>())> { private: size_t m_Size; std::vector<T> list; public: Orders(const size_t& sz, const std::vector<T>& l) : list(l), m_Size(sz), p_queue<T>(&(*list.begin()), &(*list.end()), less<T>()){} virtual const size_t getSize() const { return m_Size; } virtual const std::vector<T> getList() const = 0; }; struct buy_orders : public Orders<size_t>{ std::vector<size_t> buy_prices; buy_orders(const size_t& sz) : Orders(sz, buy_prices) {} }; 
//p_queue.h #include <functional> template<typename T, typename Compare = std::less<T>> class p_queue{ protected: T* m_first; T* m_last; Compare m_comp; public: p_queue(T* first, T* last, Compare comp = Compare()) : m_first(first), m_last(last), m_comp(comp) {} }; 

The above code produces the error:

<source>: In instantiation of 'Orders<T>::Orders(const size_t&, const std::vector<T>&) [with T = long unsigned int; size_t = long unsigned int]': <source>:39:57: required from here <source>:31:59: error: type 'p_queue<long unsigned int, std::less<long unsigned int> >' is not a direct base of 'Orders<long unsigned int>' 31 | p_queue<T>(&(*list.begin()), &(*list.end()), less<T>()){} | ^ <source>:31:59: error: no matching function for call to 'p_queue<long unsigned int, less<long unsigned int> >::p_queue()' <source>:13:5: note: candidate: 'p_queue<T, Compare>::p_queue(T*, T*, Compare) [with T = long unsigned int; Compare = less<long unsigned int>]' 13 | p_queue(T* first, T* last, Compare comp = Compare()) : | ^~~~~~~ <source>:13:5: note: candidate expects 3 arguments, 0 provided <source>:7:7: note: candidate: 'constexpr p_queue<long unsigned int, less<long unsigned int> >::p_queue(const p_queue<long unsigned int, less<long unsigned int> >&)' 7 | class p_queue{ | ^~~~~~~ <source>:7:7: note: candidate expects 1 argument, 0 provided <source>:7:7: note: candidate: 'constexpr p_queue<long unsigned int, less<long unsigned int> >::p_queue(p_queue<long unsigned int, less<long unsigned int> >&&)' <source>:7:7: note: candidate expects 1 argument, 0 provided ASM generation compiler returned: 1 <source>: In instantiation of 'Orders<T>::Orders(const size_t&, const std::vector<T>&) [with T = long unsigned int; size_t = long unsigned int]': <source>:39:57: required from here <source>:31:59: error: type 'p_queue<long unsigned int, std::less<long unsigned int> >' is not a direct base of 'Orders<long unsigned int>' 31 | p_queue<T>(&(*list.begin()), &(*list.end()), less<T>()){} | ^ <source>:31:59: error: no matching function for call to 'p_queue<long unsigned int, less<long unsigned int> >::p_queue()' <source>:13:5: note: candidate: 'p_queue<T, Compare>::p_queue(T*, T*, Compare) [with T = long unsigned int; Compare = less<long unsigned int>]' 13 | p_queue(T* first, T* last, Compare comp = Compare()) : | ^~~~~~~ <source>:13:5: note: candidate expects 3 arguments, 0 provided <source>:7:7: note: candidate: 'constexpr p_queue<long unsigned int, less<long unsigned int> >::p_queue(const p_queue<long unsigned int, less<long unsigned int> >&)' 7 | class p_queue{ | ^~~~~~~ <source>:7:7: note: candidate expects 1 argument, 0 provided <source>:7:7: note: candidate: 'constexpr p_queue<long unsigned int, less<long unsigned int> >::p_queue(p_queue<long unsigned int, less<long unsigned int> >&&)' <source>:7:7: note: candidate expects 1 argument, 0 provided 

If I remove the buy_orders struct from orders.h, the problem disappears. Why is this? How can I solve this issue?

6
  • 2
    There is already std::less. So this is ambiguous. Commented Oct 19, 2022 at 6:22
  • I know of the similar question which is recommended. But maybe I am misunderstanding something here, in the other question the error arises because 'derived2' clearly isn't inheriting from the base class, because it inherits from 'derived1' which then inherits from base. But in this case my 'Orders' class gets the error, even though it is directly inheriting from the base class. Commented Oct 19, 2022 at 6:35
  • @SergeBallesta No, the fundamental problem is still the same which is that p_queue<T> is not a direct base of Orders while p_queue<T, decltype(less<T>())> is a direct base. And since you reopened the question, I've explained the reason in my answer. Commented Oct 19, 2022 at 6:44
  • @proxylite You're actually inheriting from p_queue<T, decltype(less<T>())> but trying to use the ctor of p_queue<T> and hence the error. Basically, p_queue<T, decltype(less<T>())> and p_queue<T> are different class types. The ctor that you use must belong to p_queue<T, decltype(less<T>())>. The solution is given in my answer below. Commented Oct 19, 2022 at 6:46
  • Yes, now I understand. So easy to miss these things sometimes... Anyway, that seems to work now. Thanks again! Commented Oct 19, 2022 at 6:49

2 Answers 2

1

You actually have 2 problems here.

First one is simply that you have to pass explicitely the second template parameter to the p_queue constructor:

... Orders(const size_t& sz, const std::vector<T>& l) : list(l), m_Size(sz), p_queue<T, decltype(less<T>())>(&(*list.begin()), &(*list.end()), less<T>()) {} ... 

is enough to make the type 'base_class' is not a direct base of 'sub_class' error disappear.

But there is still another problem, even if it only raises a warning. Base classes are initialized before data members in C++, no matter the order of initializers. That means that p_queue constructor will be called before initialization of list, leading to Undefined Behaviour, because it explicitely uses the first and last members before they get a chance to be initialized. That means that you have to set up a 2 phases initialization of p_queue, with a default initialization first, and then set its m_first and m_last members in Orders constructor body:

... p_queue(Compare comp = Compare()) : m_comp(comp) {} p_queue(T* first, T* last, Compare comp = Compare()) : m_first(first), m_last(last), m_comp(comp) {} ... 

and

... Orders(const size_t& sz, const std::vector<T>& l) : list(l), m_Size(sz), p_queue<T, decltype(less<T>())>(less<T>()) { this->m_first = &(*list.begin()); this->m_last = &(*list.end()); } ... 
Sign up to request clarification or add additional context in comments.

2 Comments

Or just not make this a class hierarchy and instead make it composition. Then you can choose the order of members.
Oh damn, I never thought about that. I will definitely try your solution. Thanks a bunch Serge! @SebastianRedl hmm yes maybe composition would be better? I didn't want to use composition here because an order doesn't have a priority queue, it is a priority queue. But maybe I'm too stuck in that line of thinking. Then I suppose the buy_orders class will have an object of orders, and the orders class an object of p_queue?
1

Problem 1

The problem is that p_queue<T> and p_queue<T, decltype(::less<T>())> are two different class-type and you're inheriting from the latter(p_queue<T, decltype(::less<T>())>) but trying to use the constructor of the former(p_queue<T>) in the member initializer list.

To solve this just make sure that you use the constructor of p_queue<T, decltype(::less<T>())> instead of p_queue<T> in the member initializer list as shown below:

template<typename T> class Orders : public p_queue<T, decltype(::less<T>())> { private: size_t m_Size; std::vector<T> list; public: Orders(const size_t& sz, const std::vector<T>& l) : list(l), m_Size(sz), //--------------vvvvvvvvvvvvvvvvvvvv---->added this second template argument p_queue<T , decltype(::less<T>())>(&(*list.begin()), &(*list.end()), ::less<T>()){} virtual const size_t getSize() const { return m_Size; } virtual const std::vector<T> getList() const = 0; }; 

Working demo


Problem 2

Additionally as base class ctor will be used before the derived class member like list is initialized, and since you're using *list.begin() as an argument to the base class ctor, the program has undefined behavior.

Clang gives a warning about this:

<source>:36:43: warning: field 'list' is uninitialized when used here [-Wuninitialized] p_queue<T , decltype(::less<T>())>(&(*list.begin()), &(*list.end()), ::less<T>()){} ^ <source>:44:36: note: in instantiation of member function 'Orders<unsigned long>::Orders' requested here buy_orders(const size_t& sz) : Orders(sz, buy_prices) {} ^ <source>:36:61: warning: field 'list' is uninitialized when used here [-Wuninitialized] p_queue<T , decltype(::less<T>())>(&(*list.begin()), &(*list.end()), ::less<T>()){} 

2 Comments

Oh god! I feel like a silly person now. I could have sworn I tried this, of course the constructor call has to have the same template parameters.... Thank you Jason, I sincerely appreciate your help.
@proxylite You're welcome. Also note that since the base class ctor will be used before list is initialized and since you're using that uninitialized list in the ctor(like *list.begin()), you have undefined behavior. You'll get a warning about this in clang. Demo.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.