C++ Template Inheritance Issues (Warnings and Errors) - c++

I'm trying to mimic the .Net implementation of a generic List in C++.
I've fleshed out the various interfaces as purely virtual abstract classes as follows:
template <typename T>
class ICollection {
public:
virtual void Add(T item) = 0;
virtual void Clear(void) = 0;
virtual bool Contains(T item) = 0;
virtual void Remove(T item) = 0;
virtual int32_t Count(void) const = 0;
};
template <typename T>
class IList : public ICollection<T> {
public:
virtual T Item(int32_t index) = 0;
virtual int32_t IndexOf(T item) = 0;
virtual void Insert(int32_t index, T item) = 0;
virtual void RemoveAt(int32_t index) = 0;
};
Now when I attempt to implement my main List class as follows:
template <typename T>
class List : public IList<T>, public ICollection<T> {
public:
List(void);
List(int32_ capacity);
// ICollection<T>
void Add(T item);
// other functions from ICollection
// IList<T>
T Item(int32_t index);
// other functions from IList
void AddRange(IList<T> items);
private:
typedef vector<T> ListType;
ListType *m_pList;
};
template <typename T>
List<T>::List(void) {
m_pList = new ListType();
}
template <typename T>
void List<T>::Insert(uint32_t index, T item) {
// Insert an entry into the list at the specified offset
m_list->insert(index, item);
}
// Implementation of other functions here...
As soon as I try to use the List<T> class as follows:
List<int32_t> myList;
A warning occurs saying:
In instantiation of 'class List<long int>':
required from here
warning: direct base 'ICollection<long int>' inaccessible in 'List<long int>' due to ambiguity [enabled by default]
class List : public IList<T>, public ICollection<T> {
^
Followed by the following error:
In instantiation of 'void List<T>::Insert(uint32_t, T) [with T = long int; uint32_t = long unsigned int]':
required from here
error: no matching function for call to 'std::vector<long int, std::allocator<long int> >::insert(uint32_t&, long int&)'
m_list->insert(index, item);
^
note: std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::insert(std::vector<_Tp, _Alloc>::iterator, const value_type&) [with _Tp = long int; _Alloc = std::allocator<long int>; std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<long int*, std::vector<long int, std::allocator<long int> > >; typename std::_Vector_base<_Tp, _Alloc>::pointer = long int*; std::vector<_Tp, _Alloc>::value_type = long int]
vector<_Tp, _Alloc>::
^
note: no known conversion for argument 1 from 'uint32_t {aka long unsigned int}' to 'std::vector<long int, std::allocator<long int> >::iterator {aka __gnu_cxx::__normal_iterator<long int*, std::vector<long int, std::allocator<long int> > >}'
If I modify the declaration of the List<T> class so as to remove the IList<T> and ICollection<T> abstract classes, no errors are generated.
I'm guessing that the way in which I'm using the templated base classes is not correct in this instance.

This issue is not directly related to templates.
class A {
public void f() {}
};
class B : public A {};
class C : public A, public B {};
int main() {
C c;
c.f(); // Error: ambiguous base class!
}
When you inherit a class, the derived class contains an object for the base class, called a base class subobject. So in my example, every B contains an A. And every C contains an A and a B. The problem is, when I try to call f as a member of C, the compiler needs to find the A subobject to call it on. But there are two subobjects with that type! One is directly inherited by C, and the other is inside the inherited B subobject. So the compiler can't figure out what I mean.
The solution in this case is to just not inherit a class twice. In my example, C doesn't need to directly inherit A, since inheriting B will provide it with an indirect A subobject and access to all its members.
In your case, List<T> doesn't need to inherit ICollection<T> directly. It's enough to just derive from IList<T>.
(In other cases, it can be useful to use "virtual inheritance", which tells the compiler "only create one base class subobject for this type, even if I indirectly inherit it more than once in some derived class". But that might be overkill for your code as it stands.)

Related

Inheritance from STL priority_queue with custom comparator not working

I would like to inherit from STL priority queue to have some additional functionality such as:
allowing removal. But I am struggling to make this work when I use custom comparators. MWE:
#include <queue>
template<typename T, class Container=std::vector<T>, class Compare=std::less<typename Container::value_type>>
class custom_priority_queue : public std::priority_queue<T, Container, Compare>
{
public:
// My additional functions here.
};
int main()
{
auto pq_comp = [](const int& a, const int& b) { return a <= b; };
std::priority_queue<int, std::vector<int>, decltype(pq_comp)> pq(pq_comp); // works
custom_priority_queue<int> pq_custom; // works
custom_priority_queue<int, std::vector<int>, decltype(pq_comp)> pq_custom2(pq_comp); // Error
return 0;
}
The error is:
main.cpp: In function ‘int main()’:
main.cpp:15:87: error: no matching function for call to ‘custom_priority_queue, main():: >::custom_priority_queue(main()::&)’
15 | custom_priority_queue<int, std::vector<int>, decltype(pq_comp)> pq_custom2(pq_comp); // Error
| ^
main.cpp:4:7: note: candidate: ‘custom_priority_queue, main():: >::custom_priority_queue(const custom_priority_queue, main():: >&)’
4 | class custom_priority_queue : public std::priority_queue<T, Container, Compare>
| ^~~~~~~~~~~~~~~~~~~~~
main.cpp:4:7: note: no known conversion for argument 1 from ‘main()::’ to ‘const custom_priority_queue, main():: >&’
main.cpp:4:7: note: candidate: ‘custom_priority_queue, main():: >::custom_priority_queue(custom_priority_queue, main():: >&&)’
main.cpp:4:7: note: no known conversion for argument 1 from ‘main()::’ to ‘custom_priority_queue, main():: >&&’
Constructors are not automatically inherited, so your class probably lacks any constructor, except the implicitly-declared ones.
You can explicitly inherit all constructors of the base class:
template<typename T, class Container=std::vector<T>, class Compare=std::less<typename Container::value_type>>
class custom_priority_queue : public std::priority_queue<T, Container, Compare>
{
// inherit constructors
using priority_queue::priority_queue;
public:
// My additional functions here.
};

Issue regarding inheritance - "type is not a base class"

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?
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());
}
...
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>()){}

Defining destructor of base class makes std::map::emplace fail

When I try to emplace an object which has a unique_ptr and a destructor into a map, I get a compiler error, shown below. However, when the object has no destructor defined, emplacement works just fine. What is going on?
How can I emplace an object which has a unique_ptr and a destructor defined into a map?
#include <map>
#include <iostream>
#include <string>
struct A {
std::string name;
std::unique_ptr<int> p;
virtual ~A(){}; // comment out this line
A(std::string n) : name(n) { }
};
int main()
{
std::map<int, A> m;
m.emplace(1, A("Name"));
return 0;
}
I'm using Apple clang version 11.0.0 on OSX
Compiler Error
In file included from main.cpp:1:
In file included from /Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/map:480:
In file included from /Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/__tree:16:
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:1826:31: error: no matching constructor for initialization of 'std::__1::pair<const int, B>'
::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:1718:18: note: in instantiation of function template specialization
'std::__1::allocator<std::__1::__tree_node<std::__1::__value_type<int, B>, void *> >::construct<std::__1::pair<const int, B>, int, B>' requested here
{__a.construct(__p, _VSTD::forward<_Args>(__args)...);}
^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:1561:14: note: in instantiation of function template specialization
'std::__1::allocator_traits<std::__1::allocator<std::__1::__tree_node<std::__1::__value_type<int, B>, void *> > >::__construct<std::__1::pair<const int, B>,
int, B>' requested here
{__construct(__has_construct<allocator_type, _Tp*, _Args...>(),
^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/__tree:2212:20: note: in instantiation of function template specialization
'std::__1::allocator_traits<std::__1::allocator<std::__1::__tree_node<std::__1::__value_type<int, B>, void *> > >::construct<std::__1::pair<const int, B>,
int, B>' requested here
__node_traits::construct(__na, _NodeTypes::__get_ptr(__h->__value_), _VSTD::forward<_Args>(__args)...);
^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/__tree:2157:29: note: in instantiation of function template specialization
'std::__1::__tree<std::__1::__value_type<int, B>, std::__1::__map_value_compare<int, std::__1::__value_type<int, B>, std::__1::less<int>, true>,
std::__1::allocator<std::__1::__value_type<int, B> > >::__construct_node<int, B>' requested here
__node_holder __h = __construct_node(_VSTD::forward<_Args>(__args)...);
^
... [similar error messages come here]
main.cpp:19:11: note: in instantiation of function template specialization 'std::__1::map<int, B, std::__1::less<int>, std::__1::allocator<std::__1::pair<const
int, B> > >::emplace<int, B>' requested here
m.emplace(1, B("Name"));
^
std::map::emplace allows to create object in place by passing constructor's arguments and you don't need copy/move.
As mentions in one of comment that because of user provided distructor, compiler does not generate move constructor implicitly.
But i have added move because otherwise it not useful any way in real world problems.
Due to this you need to create object in place.
Using following code.
#include <iostream>
#include <map>
#include <memory>
struct A {
std::string name;
std::unique_ptr<int> p;
~A(){}
A(std::string n) : name(n) { }
A(A&& other): name(std::move(other.name)), p(std::move(other.p)){}
};
int main(int , char *[]){
std::map<int, A> m;
m.emplace(1, "Name");
for(const auto& e: m)
cout<< e.first<< ", "<< e.second.name<<'\n';
}
output: 1, Name

Can derived object cast to base reference in c++ template class?

all. I am trying to learn something about template inherit. I want to cast a temp derived object to its base reference. But I come across this problem:
typedef long size64_t;
//////Base class
template <typename _Scalar>
class MatrixBase{
public:
virtual _Scalar operator()(size64_t rowid, size64_t colid) = 0;
};
//////Derived class 1
template <typename _Scalar>
class MatrixHolder : public MatrixBase<_Scalar>{
public:
MatrixHolder(){};
inline _Scalar operator()(size64_t rowid, size64_t colid){return 0;}
};
//////Derived class 2
template <typename _Scalar>
class Matrix : public MatrixBase<_Scalar>{
public:
Matrix(){};
inline _Scalar operator()(size64_t rowid, size64_t colid){return 0;}
};
//////The function with parameters as Base class reference, wanting to get derived object as input.
template <typename _Scalar>
MatrixHolder<_Scalar> apply(MatrixBase<_Scalar>& lhs, MatrixBase<_Scalar>& rhs){
MatrixHolder<_Scalar> result;
return result;
}
and in main, we have:
void main(){
Matrix<double> m1;
Matrix<double> m2;
apply(m1, m2);//Sucess
apply(m1, apply(m1, m2));//Fail
}
the compiler said:
note: candidate function [with _Scalar = double] not viable: no known conversion from
'MatrixHolder<double>' to 'MatrixBase<double> &' for 2nd argument
MatrixHolder<_Scalar> apply(MatrixBase<_Scalar>& lhs, MatrixBase<_Scalar>& rhs){
^
1 error generated.
apply(m1, apply(m1, m2));//Fail
The problem here is that the inner apply returns a temporary, which cannot bind to the non-const reference parameter of the outer apply.
If you can make the parameters MatrixBase<_Scalar> const& it would be a possible match.

Implicit conversion of shared_ptr

I have two shared_ptrs of the classes U and T where T is the base of U.
It is no problem to do an implicit conversion from shared_ptr<U> to shared_ptr<T>.
But is is also possible to do a conversion from shared_ptr<T> to shared_ptr<U> ?
I tried the sugested solution:
class T {
public:
virtual ~T() {}
protected:
void fillData() = 0;
};
class Timpl : public T
{
public:
virtual ~Timpl() {}
protected:
virtual void fillData() = 0;
};
class U : public Timpl {
public:
virtual ~U() {}
protected:
virtual void fillData() {...} // fill
};
typedef shared_ptr<T> TPtr
typedef shared_ptr<U> UPtr
TPtr tp = std::make_shared<U>();
UPtr up = std::static_pointer_cast<U>(tp); //<-- error here :
error: no matching function for call to 'static_pointer_cast(TPtr)'
note: template std::__shared_ptr<_Tp1, _Lp> std::static_pointer_cast(const std::__shared_ptr<_Tp2, _Lp>&)
note: template argument deduction/substitution failed:
note: 'TPtr {aka boost::shared_ptr}' is not derived from 'const std::__shared_ptr<_Tp2, _Lp>'
Solution for this issue:
mixing of std::shared_ptr with boost::shared_ptr is no good idea.
Yes, use static_pointer_cast:
#include <memory>
struct T { virtual ~T() {} };
struct U : T {};
std::shared_ptr<T> pt = std::make_shared<U>(); // *pt is really a "U"
auto pu = std::static_pointer_cast<U>(pt);
There's also a matching std::dynamic_pointer_cast which returns a null pointer if the conversion isn't possible.