Random access priority queue - c++

Continuing List to priority queue
I'm implementing a improved priority_queue with random access.
template <class T, class Container = std::vector<T> >
class Heap {
public:
Heap() {}
Heap(const Container& container) {
container_ = container;
std::make_heap(container_.begin(), container_.end());
}
Heap<T, Container>& operator=(const Heap<T, Container>& heap) {
if (this != &heap)
container_ = heap.container_;
return *this;
}
void push(const T& x) {
container_.push_back(x);
std::push_heap(container_.begin(), container_.end());
}
void pop() {
std::pop_heap(container_.begin(), container_.end());
container_.pop_back();
}
const T& top() {
return container_.front();
}
const Container& getContainer() const {
return container_;
}
T& operator[](size_t n) {
return container_[n];
}
typename Container::const_iterator begin() const {
return container_.begin();
}
typename Container::const_iterator end() const {
return container_.end();
}
size_t size() const {
return container_.size();
}
T& base() {
return container_.back();
}
Container::iterator erase(Container::iterator position) {
return container_.erase(position);
}
private:
Container container_;
};
Am I taking the right way?
Fixed the unary constructor.
Improved code.

Doesn't look that great to me:
The unary constructor should take argument by const reference.
The assignment operator doesn't check for self-assignment.
The getContainer() method shows a lack of clarity in the interface - why would you simply expose the implementation detail like that?
Most importantly: why do you want a "random access priority queue"?

Your pop() method can violate the heap ordering. Use pop_heap() instead of pop_back(). I can't seem to find any other bug right now.
You can easily test such an implementation by pushing in a random integers and pop() them one by one. You should get them back in sorted order. This is known as heap sort.
Suggestions:
Instead of returning a ref to the container you could implement an const iterator to this class.
Note that you should not change the key of the randomly accessed element because it may destroy the heap structure. If you need such functionality you should implement a change_key function which would change the key safely and maintain the heap ordering.

Related

Iterator over pure virtual container interface in C++

I have a pure virtual interface to a container which is more or less like this:
class IContainer
{
public:
virtual ~IContainer() = default;
virtual Element& operator[](size_t index) = 0;
virtual const Element& operator[](size_t index) const = 0;
virtual size_t size() const = 0;
};
I would like to make use of range for loops, so I need to define begin() and end(). In order to do so, I need to define the iterator type as well.
It should be not be particularly hard, but nevertheless I would like to know if is there already anything in STL or Boost that can come to help, before I start coding something that already exists.
It might not be a good idea, but adding for(:) loop support is relatively easy here. I'll be minimal.
I'll create an iteroid, a not-iterator that is enough to support for(:) loops. This requires ++, != and unary * support, and nothing else.
template<class C>
struct index_iteroid {
decltype(auto) operator*()const {
return (*container)[i];
}
index_iteroid(index_iteroid const&)=default;
index_iteroid& operator=(index_iteroid const&)=default;
friend bool operator==(index_iteroid const& lhs, index_iteroid const& rhs) {
return std::tie(lhs.i, lhs.container)==std::tie(rhs.i, rhs.container);
}
friend bool operator!=(index_iteroid const& lhs, index_iteroid const& rhs) {
return !(lhs==rhs);
}
void operator++()&{
++i;
}
index_iteroid(C* c, std::size_t in):i(in), container(c) {}
private:
std::size_t i = 0;
C* container = nullptr;
};
now we use it:
class IContainer
{
public:
virtual ~IContainer() = default;
virtual Element& operator[](size_t index) = 0;
virtual const Element& operator[](size_t index) const = 0;
virtual size_t size() const = 0;
index_iteroid<IContainer> begin() { return {this, 0}; }
index_iteroid<IContainer> end() { return {this, size()}; }
index_iteroid<IContainer const> begin() const { return {this, 0}; }
index_iteroid<IContainer const> end() const { return {this, size()}; }
};
and there you have it.
void test( IContainer* cont ) {
if (!cont) return;
for(Element& e : *cont) {
// code
}
}
please excuse any typos.
Now a full iterator takes about 2-3 times as much code as my iteroid does, but nothing tricky, just annoying boilerplate mostly.
The standard doesn't have much to help you. For boost, you could compose a counting iterator with a function caling iterator/generator, and have the function call use []. Boost also has some utilities to make it take less boilerplate to write a full iterator, if you want to upgrade the iteroid to an iterator.
C++ doesn't do "interfaces" like this. The idiomatic way is for the (potential) clients of IContainer to instead be templated over the container type and just call values[index], or be templated over an iterator type and call like *(first + offset).
In C++20 you will be able to write a Container Concept, which behaves somewhat like an interface definition, but you can already express a Concept as documented requirements.
If instead you want a type-erased random access "container", you can use boost::any_range<Element, boost::random_access_traversal_tag>

Wrap std::vector class to make queries look like .net List<T> queries

When i switched to c++ from c#, one of the most missed functionalities was the List class queries like - find, findall, exists, etc., combined with lambda predicates.
So, i decided to wrap the vector class and enjoy the "syntactic sugar" of the result.
I'm aware that not all vector members are rewritten and this can serve just as example of my idea.
The question is:
Is this a viable solution of my problem?
Are there conceptual mistakes that would ruin performance or the behavior of the wrapped class?
Here the wrapper looks like:
#include<vector>
template<typename T>
class VectorWrap
{
public:
std::vector<T> v;
VectorWrap() noexcept:v{} {}
VectorWrap(std::initializer_list<T> list)noexcept : v{ list } {}
T& operator[](int i) noexcept { return v[i]; }
const T& operator[](int i) const noexcept { return v[i]; }
void add(const T& a) noexcept { v.push_back(a); }
void push_back(const T& a) noexcept { v.push_back(a); }
void clear() noexcept { v.clear(); }
const auto size() const noexcept { return v.size(); }
const auto begin() const noexcept { return v.begin(); }
const auto end() const noexcept { return v.end(); }
const bool empty() const noexcept { return v.empty(); }
bool contains(const T& a) const noexcept
{
auto x = std::find(v.begin(), v.end(), a);
return x != std::end(v);
}
template<typename Pred>
T find(Pred p) const noexcept
{
auto x = std::find_if(v.begin(), v.end(), p);
return x != std::end(v) ? *x : T{};
}
template<typename Pred>
bool any(Pred p) const noexcept
{
return std::any_of(std::begin(v), std::end(v), p);
}
template<typename Pred>
VectorWrap<T> findall(Pred p) noexcept
{
VectorWrap res;
for (auto* x : v)
if (p(x)) res.push_back(x);
return res;
}
template<typename Pred>
const T& minby(Pred p) const noexcept
{
if (v.size() == 0)
return T{};
return *std::min_element(v.begin(), v.end(), p);
}
template<typename Pred>
const T& maxby(Pred p) const noexcept
{
if (v.size() == 0)
return T{};
return *std::max_element(v.begin(), v.end(), p);
}
};
Edit:
For all those in the comments that seems like didn't catch the idea behind my question.
I don't claim to completely replace any container class. I just made an example for few functionalities of the vector that i use a lot. For the same purpose i made the underlying vector public.
But that's not the point. The real point was the "syntactic sugar" behind. The possibility to type:
VectorWrap<int> MyWrappedExampleVectorInstance = { 1, 2, 3, 4 };
int result = MyWrappedExampleVectorInstance.find([&](int i) { return i > 1 && i < 5; });
instead of:
std::vector<int> MyNonWrappedExampleVectorInstance = { 1, 2, 3, 4 };
std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>> firstMatchOnLambdaPredicate = std::find_if(MyNonWrappedExampleVectorInstance.begin(), MyNonWrappedExampleVectorInstance.end(), [&](int i) { return i > 1 && i < 5; });
int result = *firstMatchOnLambdaPredicate;
I hope that you can see the difference of the readability and the conciseness between both examples.
In C++ ideology, a great emphasis is put on the separation between data and algorithm. This is why vector doesn't have the find method - the simple sequential search works same way for vectors, lists and queues.
Because of that, there is no need to duplicate the sequential search logic in all those containers - instead, a sequential search find function is written once (called algorithm) and made in a such way that it is independent of any container which is capable of sequence iteration. This provides for a plethora of benefits - no need to repeat the same code in multiple containers, if improvement is made in the algorithm, it automatically becomes available to all containers and the rest of benefits which are usually associated with avoidance of code duplication.
This doesn't work always like that - for example, associative containers (sets, maps) do have a method find defined on them, since their search operations are not sequential lookups and require knowledge of data representation to be efficient.
The one thing I personally do not like about STL algos is that they historically take a pair of iterators, and it is too easy to make a mistake by providing a pair of unrelated iterators. But this is solved with Ranges.

Automatically create iterator from class that implements operator[]

Suppose I have a class that implements operator[], e.g.:
class Array {
public:
Array(size_t s) : data(new int[s]) {}
~Array() { delete[] data; }
int& operator[](size_t index) {
return data[index];
}
private:
int* data;
};
Is there a way to create a random access iterator from the class without having to manually create the iterator class and all its methods? I could manually define the class as follows:
class ArrayIterator : public std::iterator<std::random_access_iterator_tag, int> {
public:
ArrayIterator(Array& a) : arr(a), index(0) {}
reference operator[](difference_type i) {
return arr[index + i];
}
ArrayIterator& operator+=(difference_type i) {
index += i;
return *this;
}
bool operator==(const ArrayIterator& rhs) {
return &arr == &rhs.arr && index == rhs.index;
}
// More methods here...
private:
Array& arr;
difference_type index;
};
But doing so is time consuming since there are so many methods to implement, and each iterator for a class with operator[] would have the exact same logic. It seems it would be possible for the compiler to do this automatically, so is there a way to avoid implementing the entire iterator?
Is there a way to create a random access iterator from the class without having to manually create the iterator class and all its methods?
The simplest way to create a random-access iterator is to just use a raw pointer, which satisfies all of the requirements of the RandomAccessIterator concept (the STL even provides a default template specialization of std::iterator_traits for raw pointers), eg:
class Array {
public:
Array(size_t s) : data(new int[s]), dataSize(s) {}
~Array() { delete[] data; }
int& operator[](size_t index) {
return data[index];
}
size_t size() const { return dataSize; }
int* begin() { return data; }
int* end() { return data+dataSize; }
const int* cbegin() const { return data; }
const int* cend() const { return data+dataSize; }
private:
int* data;
size_t dataSize;
};
Implementing the random access operator by using operator[] may work, but it can be very inefficient and that's why compilers dont do that automatically. Just imagine adding operator[] to a class like std::list, where "going to element i" may take up to i steps. Incrementing an iterator based on operator[] would then have complexity O(n), where n is the size of the list. However, users of random access iterators, expect a certain efficiency, typically O(1).

avoiding dangling reference in array subscription operator

How to avoid dangling reference in array subscription operator in some vector implementation below? If realloc changes the pointer then references previously obtained from operator[] are no longer valid. I cannot use new/delete for this. I have to use malloc/realloc/free.
template <class T>
class Vector
{
public:
typedef size_t size_type_;
...
T& operator[] (const size_type_);
void push_back (const T&);
...
private:
const size_type_ page_size_;
size_type_ size_;
size_type_ capacity_;
T* buffer_;
};
template<class T>
inline
T&
some_namespace::Vector<T>::operator[] (const size_type_ index)
{
ASSERT(index < size_);
return buffer_[index];
}
template<class T>
inline
void
some_namespace::Vector<T>::push_back(const T& val)
{
if (size_ >= capacity_)
{
capacity_ += page_size_;
T* temp = (T*)(realloc(buffer_, capacity_*sizeof(T)));
if (temp != NULL)
{
buffer_ = temp;
}
else
{
free(buffer_);
throw some_namespace::UnexpectedEvent();
}
}
buffer_[size_++] = val;
}
By the way, the source of dangling reference in the code was this:
v_.push_back(v_[id]);
where v_ is an instance of Vector. To guard against this the new push_back is:
template<class T>
inline
void
some_namespace::Vector<T>::push_back(const T& val)
{
if (size_ >= capacity_)
{
const T val_temp = val; // copy val since it may come from the buffer_
capacity_ += page_size_;
T* temp = (T*)(realloc(buffer_, capacity_*sizeof(T)));
if (temp != NULL)
{
buffer_ = temp;
}
else
{
free(buffer_);
throw some_namespace::UnexpectedEvent();
}
buffer_[size_++] = val_temp;
}
else
{
buffer_[size_++] = val;
}
}
There are basically three things you can do:
Accept it as is and document it. This is what the STL does and it's actually quite reasonable.
Don't return a reference, return a copy. Of course, this means you can't assign to the vector's element anymore (v[42] = "Hello"). In that case you want a single operator[] which is marked const.
Return a proxy object which stores a reference to the vector itself and the index. You add an assignment to the proxy from const T& to allow writing to the vector's element and you need to provide some way to access/read the value, most likely an implicit operator const T&. Since the proxy object asks the vector on each access for the current position of the element, this works even if the vector changed the location of those elements between calls. Just be careful with multi-threading.
Here's a sketch for the proxy, untested and incomplete:
template<typename T> class Vector
{
private:
// ...
// internal access to the elements
T& ref( const size_type_ i );
const T& ref( const size_type_ i ) const;
class Proxy
{
private:
// store a reference to a vector and the index
Vector& v_;
size_type_ i_;
// only the vector (our friend) is allowed to create a Proxy
Proxy( Vector& v, size_type_ i ) : v_(v), i_(i) {}
friend class Vector;
public:
// the user that receives a Proxy can write/assign values to it...
Proxy& operator=( const T& v ) { v_.ref(i_) = v; return *this; }
// ...or the Proxy can convert itself in a value for reading
operator const T&() const { return v_.ref(i_); }
};
// the Proxy needs access to the above internal element accessors
friend class Proxy;
public:
// and now we return a Proxy for v[i]
Proxy operator[]( const size_type_ i )
{
return Proxy( *this, i );
}
};
Note that the above is incomplete and the whole technique has some drawbacks. The most significant problem is that the Proxy "leaks" in the API and therefore some use cases are not met. You need to adapt the technique to your environment and see if it fits.

How to assign two containers having different template parameters

Let's suppose that I have a class container:
template<class T, int size>
class container
{
private:
T* data;
int length;
public:
container()
{
data=new T[size];
length=size;
}
~container()
{
if(length>0)
delete[] data;
}
container& operator= (container<T,size> c)
{
if(length>0)
delete[] data;
data=new T[c.length];
length=c.length;
for(int i=0; i<length; i++)
data[i]=c.data[i];
return *this;
}
};
The problem is that if I have two container of different size, I can't use the = operator to assign one to the other.For example:
container<int,4> c1;
container<int,5> c2;
c1=c2; // syntax error: 4!=5
Classes like c++11 array allow to do this.
How to do that?
Templates are just that -- templates the compiler uses to make classes, not classes themselves.
Thus, container<int,4> and container<int,5> are completely separate classes, with all of the access restrictions that implies.
In particular, this means that container<int, 4>'s assignment operator cannot access the private memebers of container<int,5>.
There are a few ways to get around this:
Eliminate the size template argument, since as others have noted, you seem to be allocating memory dynamically, so nailing down the size at compile time isn't adding any value, and in fact could be misleading since your assignment operator could result in a different size than the one declared.
Implement your assignment operator in terms of container's public interface.
Declare all container classes of the same type as friends by adding the following line to your class:
Code:
template<class U, int otherSize> friend class Foo;
and declaring your assignment operator as follows:
template <int otherSize>
container<T,size>& operator=(container<T,otherSize> c);
You need to parametrize your assigment operator with size of container you want to assign from (ignoring any other issues with posted code):
template <int otherSize>
container& operator= (container<T,otherSize> c)
{
if(length>0)
delete[] data;
data=new T[otherSize];
length=otherSize;
for(int i=0; i<otherSize; i++)
data[i]=c.data[i];
return *this;
}
As commenters have pointed out, having the initial size be a template parameter looks wrong, and is the source of your problems.
But considering the general question of assigning from two incompatible containers, you could have a templated operator= as tumdum's answer shows, but that doesn't help when you want to fill the container from another, slightly-different type.
The standard containers solve the problem by allowing construction and assignment from a range, defined by a pair of iterators:
template<typename FwdIter>
container(FwdIter begin, FwdIter end)
{
data=new T[length = std::distance(begin, end)];
std::copy(begin, end, data);
}
template<typename FwdIter>
void
assign(FwdIter begin, FwdIter end)
{
container(begin, end).swap(*this);
}
void swap(container& c)
{
std::swap(data, c.data);
std::swap(length, c.length);
}
iterator begin() { return data; }
const_iterator begin() const { return data; }
const_iterator cbegin() const { return data; }
iterator end() { return data+size; }
const_iterator end() const { return data+size; }
const_iterator cend() const { return data+size; }
Now you can do:
container<int,4> c1;
container<int,5> c2;
c1.assign(c2.begin(), c2.end());