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?
CodePudding user response:
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());
}
...
CodePudding user response:
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;
};
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>()){}