How do you use vector as a base class - c++

Win7
Cygwin
This the first time I've used templates & containers. I don't understand the errors. To my (naive) way of looking at things, I have defined an allocator (_Alloc) and a typdef (allocator_Type). The messages appear to be saying that I haven't done my homework properly. I have no clue as to what to do.
The code is:
template<typename T, typename _Alloc = std::allocator<T>>
class Array : public vector<T, _Alloc> {
public:
typedef T value_type;
typedef _Alloc allocator_Type;
private:
int compar (const void* p1, const void* p2) { T v1 = (T)*p1;
T v2 = (T)*p2;
return (v1 < v2)? -1: (v1 > v2)? 1: 0; }
public:
explicit Array (const allocator_Type& alloc = allocator_type()) : vector<T, _Alloc>(alloc) { };
explicit Array (size_t n) : vector<T, _Alloc>(n) { };
Array (size_t n, const T& val,
const allocator_type& alloc = allocator_type()): vector<T, _Alloc>(n, val, alloc) { };
};
The error messsages are:
main.cpp:31:27: error: 'allocator_type' does not name a type
const allocator_type& alloc = allocator_type()): vector<T, _Alloc>(n, val, alloc) { };
^
main.cpp:31:27: note: (perhaps 'typename std::vector<_Tp, _Alloc>::allocator_type' was intended)
main.cpp:31:66: warning: ISO C++ forbids declaration of 'alloc' with no type [-fpermissive]
const allocator_type& alloc = allocator_type()): vector<T, _Alloc>(n, val, alloc) { };
^
main.cpp:28:65: warning: there are no arguments to 'allocator_type' that depend on a template parameter, so a declaration of 'allocator_type' must be available [-fpermissive]
explicit Array (const allocator_Type& alloc = allocator_type()) : vector<T, _Alloc>(alloc) { };
^
main.cpp:31:66: warning: there are no arguments to 'allocator_type' that depend on a template parameter, so a declaration of 'allocator_type' must be available [-fpermissive]
const allocator_type& alloc = allocator_type()): vector<T, _Alloc>(n, val, alloc) { };
^

This has been stated in comments but not posted as an answer yet: you don't.
The standard library containers are not supposed to be used as (public) base classes. They are non-polymorphic, so you can't pass an object derived from one safely to any function which expects a pointer or reference to the container.
You could , in theory, use a standard container as private base class. This has the same semantics as a private member variable, but it would make your code a lot easier to follow for others if you just used a private member variable.

First - you have a typo: uppercase T in allocator_Type and lowercase in const allocator_Type& alloc = allocator_type(). Second - std::vector destructor is not virtual, hence you really should not derive from it unless you never cast and delete via base class.

Related

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

c++98 use iterator constructor only if InputIt is an iterator of type T

For a school project I have to implement std::vector but only using C++98 standard.
The problem is that the size constructor and iterator constructor are conflicting with each other when I call it with a signed integer, so I came up with this ( whith my own implementations of enable_if, is_same, and iterator_traits):
// Size constructor
explicit vector(
size_type count,
const T &value = T(),
const Allocator &alloc = Allocator()
) : _allocator(alloc),
_capacity(count),
_size(count),
_array(_allocator.allocate(_capacity)) {
std::fill(begin(), end(), value);
}
// Iterator constructor
template <
class InputIt
> vector(
InputIt first, InputIt last,
const Allocator &alloc = Allocator(),
typename ft::enable_if< ft::is_same< typename ft::iterator_traits< InputIt >::value_type, T >::value, int >::type = 0
) : _allocator(alloc),
_capacity(std::distance(first, last)),
_size(_capacity),
_array(_allocator.allocate(_capacity)) {
std::copy(first, last, begin());
}
But now I have a problem with my implementation of iterator_traits: when I call it with an int of course it doesn't work because int doesn't have iterator member types, but when I look at cppreference about iterator_traits, it says that If Iter does not have all five member types difference_type, value_type, pointer, reference, and iterator_category, then this template has no members by any of those names (std::iterator_traits is SFINAE-friendly) (since C++17) (until C++20) which means that the check isn't implemented before C++17, so how does the real std::vector check for Iterator validity even before C++11?
Here is the compiler error I get when calling the constructor with 2 ints:
/home/crochu/Documents/42/ft_containers/iterator_traits.hpp:22:20: error: type 'int' cannot be used prior to '::' because it has no members
typedef typename Iter::difference_type difference_type;
^
/home/crochu/Documents/42/ft_containers/vector.hpp:78:55: note: in instantiation of template class 'ft::iterator_traits<int>' requested here
typename ft::enable_if< ft::is_same< typename ft::iterator_traits< InputIt >::value_type, T >::value, int >::type = 0
^
/home/crochu/Documents/42/ft_containers/main.cpp:19:20: note: while substituting deduced template arguments into function template 'vector' [with InputIt = int]
ft::vector< int > v(5, 42);
^
In file included from /home/crochu/Documents/42/ft_containers/main.cpp:13:
In file included from /home/crochu/Documents/42/ft_containers/ft_containers.hpp:15:
/home/crochu/Documents/42/ft_containers/iterator_traits.hpp:23:20: error: type 'int' cannot be used prior to '::' because it has no members
typedef typename Iter::value_type value_type;
^
/home/crochu/Documents/42/ft_containers/iterator_traits.hpp:24:20: error: type 'int' cannot be used prior to '::' because it has no members
typedef typename Iter::pointer pointer;
^
/home/crochu/Documents/42/ft_containers/iterator_traits.hpp:25:20: error: type 'int' cannot be used prior to '::' because it has no members
typedef typename Iter::reference reference;
^
/home/crochu/Documents/42/ft_containers/iterator_traits.hpp:26:20: error: type 'int' cannot be used prior to '::' because it has no members
typedef typename Iter::iterator_category iterator_category;
^
5 errors generated.
As an example, the implementation of this constructor in libstdc++ is located in the header bits/stl_vector.h:
template<typename _InputIterator>
vector(_InputIterator __first, _InputIterator __last,
const allocator_type& __a = allocator_type())
: _Base(__a)
{
// Check whether it's an integral type. If so, it's not an iterator.
typedef typename std::__is_integer<_InputIterator>::__type _Integral;
_M_initialize_dispatch(__first, __last, _Integral());
}
This is a tag dispatch using a proto-std::integral_constant class, to one of these functions:
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 438. Ambiguity in the "do the right thing" clause
template<typename _Integer>
void
_M_initialize_dispatch(_Integer __n, _Integer __value, __true_type)
{
this->_M_impl._M_start = _M_allocate(_S_check_init_len(
static_cast<size_type>(__n), _M_get_Tp_allocator()));
this->_M_impl._M_end_of_storage =
this->_M_impl._M_start + static_cast<size_type>(__n);
_M_fill_initialize(static_cast<size_type>(__n), __value);
}
// Called by the range constructor to implement [23.1.1]/9
template<typename _InputIterator>
void
_M_initialize_dispatch(_InputIterator __first, _InputIterator __last,
__false_type)
{
_M_range_initialize(__first, __last,
std::__iterator_category(__first));
}
I'd say that's about as elegant as you could get under your constraints!

Creating tuple with allocator c++

I'm learing tuples in C++ and for now I'm trying to create tuple using allocator from libcxx
template <class _Alloc>
LIBCPP_INLINE_VISIBILITY
tuple(allocator_arg_t, const _Alloc& __a, const _Tp& ... __t)
for instance:
std::allocator<int> myAllocator;
std::tuple<int> t(std::allocator_arg, myAllocator, 2);
but seems string above called
template <class Alloc>
tuple(allocator_arg_t, const Alloc& a, const Types&...);
what should I change for this?
As well, there is one row that isn't clear for me:
explicit
tuple(_Up&&... __u)
how does this call?
When you look into your implementation's source and see
namespace std {
// Other things
template <typename ... _Tp>
class tuple {
// More things
template <class _Alloc>
LIBCPP_INLINE_VISIBILITY
tuple(allocator_arg_t, const _Alloc& __a, const _Tp& ... __t)
// an implementation of this constructor
};
}
That is the constructor that cppreference names
template <class Alloc>
tuple(allocator_arg_t, const Alloc& a, const Types&...);
Your implementation has chosen to use names that are reserved for its use. What exactly those names are doesn't matter to the compiler.
what const _Tp& ... __t is?
It's a parameter pack of elements to copy into the tuple. For std::tuple<int>, it is const int&, for std::tuple<std::string, bool, char> it is const std::string &, const bool &, const char &. __t is the name of the parameter pack. C++ allows templates to have different numbers of parameters.
what about tuple(_Up&&... __u)?
That's overload (3)
Converting constructor. Initializes each element of the tuple with the corresponding value in std::forward<UTypes>(args).
This overload only participates in overload resolution if sizeof...(Types) == sizeof...(UTypes) and sizeof...(Types) >= 1 and std::is_constructible<Ti, Ui&&>::value is true for all i.
The constructor is explicit if and only if std::is_convertible<Ui&&, Ti>::value is false for at least one i.
E.g. for std::tuple<int> tup('a');, tup would be initialised by matching UTypes... with char, and the first member would have the numeric value of 'a' (97 on most platforms).
Note that there isn't much point in using an allocator-aware constructor for std::tuple<int>, because int is not a allocator-aware type. Those constructors exist for cases like
using statefully_allocated = std::vector<int, my_stateful_allocator<int>>;
my_stateful_allocator<int> alloc1 = /* something */
statefully_allocated source(alloc);
my_stateful_allocator<int> alloc2 = /* something else */
std::tuple<statefully_allocated, char> tup(std::allocator_arg, alloc2, source, 'a');
Where the statefully_allocated member copies the contents of source, but uses a copy of alloc2 to allocate. the char member is just an ordinary char, alloc2 plays no part in it's construction. See Uses-allocator construction

c++ Weird linker error happens when passing some arguments to a template

I have been trying to implement std::vector on my own. And I come across this weird linker problem, which occurs when I pass some arguments to the template . When passed with some other arguments, the code works just fine. I am really confused what goes wrong here, and don't know what keyword I can use to google with. The problem description goes as follows:
//vector.hpp: all the following is inside namespace wyf
template<typename Tp, typename Allocator = std::allocator<Tp>>
class vector {
public:
using value_type = Tp;
using allocator_type = Allocator;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = typename std::allocator_traits<Allocator>::pointer;
using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
// linker error occurs when calling this ctor with certain arguments
vector(size_type n, const_reference val, const allocator_type& alloc = allocator_type());
// other ctors
vector() : begin_(nullptr), end_(nullptr), capacity_() { }
vector(size_type n, const_reference val, const allocator_type& alloc = allocator_type());
explicit vector(size_type n);
template<typename IterTp>
vector(IterTp beg_, IterTp end_, const allocator_type& alloc = allocator_type());
vector(const vector& other, const allocator_type& alloc = allocator_type());
vector(vector&& other);
vector(vector&& other, const allocator_type& alloc);
vector(std::initializer_list<value_type> init_list, const allocator_type& alloc = allocator_type());
// omitting other member functions.
private: // three data members to hold the objects and raw memory
pointer begin_; // using pointer = value_type*;
pointer end_;
// Empty Base Optimization Helper class to hold both Allocator and capacity_
EmptyBaseOptimizationHelper<allocator_type> capacity_;
}
// still in vector.hpp, the implemention goes below
template<typename Tp, typename Allocator>
vector<Tp, Allocator>::vector(std::size_t n, const Tp& val, const Allocator& alloc) :
begin_(nullptr), end_(nullptr), capacity_(nullptr, alloc) {
begin_ = (get a pointer to allocator)->allocate(n*2);// allocate raw memory
end_ = begin_ + n;
capacity_.capacity_ = begin_ + n*2;
for (auto p = begin_; p != end_;)
(get a pointer to allocator)->construct(p++, val);
}
That's all for vector.hpp.(I used some pseudocode to make it more readable). When I do some testing, the weird linker error present itself.
#include "vector.hpp"
int main() {
wyf::vector<int> v2(10, 20);
}
When I compile with g++/clang++, the following linker error pops up:
vector.cc:(.text+0x66): undefined reference to `wyf::vector<int, std::allocator<int> >::vector<int>(int, int, std::allocator<int> const&)'
It seems that wyf::vector v2(10, 20); is regarded as calling a ctor with signature wyf::vector(int, int, std::allocator const&). But what I declared and implemented is wyf::vector(std::size_t, const int&, std::allocator const&). The linker is designed to fail when I try to link something that I didn't actually implement. Hence the linker error.
With these in mind, I tried the following call
wyf::vector<int> v3(static_cast<std::size_t>(10), 20);
And it worked just fine. Moreover, I find that, as long as I pass arguments that will trigger a conversion(implicit or explicit), the linker won't complain at all:
wyf::vector<int> v4(10.0, 20);
wyf::vector<int> v5(10, 20.0);
The code above compile and link with no error. I am really confused here. Can anyone tell me why all these happen and what I can do to make wyf::vector v(10, 20) work as expected.
As I was trying to reproduce the problem, I found out where the problem is.
The problem here is constructor overload resolution chooses a constructor have not implemented yet.
wyf::vector<int> v1(10, 20);
The call above is supposed to call the following constructor through a implicit conversion from int to size_type(std::size_t) conducted on 10:
vector(size_type n, const_reference val, const allocator_type& alloc = allocator_type());
But instead, compiler sees a better match, and chooses:
template<typename IterTp>
vector(IterTp beg_, IterTp end_, const allocator_type& alloc = allocator_type());
and instantiates wyf::vector (int, int, const std::allocator&) with IterTp being int. Since I have not implemented that constructor yet, the code compiles but fail to link. The following code succeeds to link:
wyf::vector<int> v3(static_cast<std::size_t>(10), 20);
wyf::vector<int> v4(10.0, 20);
wyf::vector<int> v5(10, 20.0);
becasue the 2 aurguments are of two different types. So the compiler cannot deduce the type of IterTp for the template version constructor, and the non-template version is chosen, which is the code works as I expected.

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.