Home > Net >  Issue regarding inheritance - "type is not a base class"
Issue regarding inheritance - "type is not a base class"

Time:10-19

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;
};

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>()){}
  • Related