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());
Related
Here is some context :
I need to overload some operators to act on std::vector operands. I decided against implementing directly the operator overloads in the global scope, as I believe it would be a bad practice due to possible conflicts. As a result, I decided to implement a templated class which :
Behaves as much as possible like a std::vector
Allows the overloading of the operator in a transparent way
The initial idea was to inherit from std::vector but I quickly realized it is not recommended. I therefore decided to create a class which holds a std::vector data member and implements an interface that makes it as transparent to the user as possible.
Here is a snippet of what I came up with :
template<typename T>
class Vector {
public:
Vector() = default;
~Vector() = default;
Vector(std::initializer_list<T> list) :data(list) {}
template<typename... Args>
Vector(Args... args) : data(args...){}
/* All std::vector functions with parameters and return value will look like this*/
template<typename...Args>
auto reserve(Args... args) {
return data.reserve(args...);
}
/* All std::vector functions with parameters and void return value will look like this*/
template<typename... Args>
void resize(Args... args) {
data.resize(args...);
}
/* All std::vector functions without parameter and void return value will look like this*/
void clear() {
data.clear();
}
/* All std::vector functions with return value will look like this*/
Vector& operator=(Vector const & other) {
data = other.getData();
return *this;
}
size_t size() const {
return data.size();
}
std::vector<T> & getData() {
return data;
}
std::vector<T> const & getData() const {
return data;
}
T & operator[](size_t i) {
return data[i];
}
T const & operator[](size_t i) const {
return data[i];
}
/* All std::vector functions without args and with return value will look like this*/
auto begin() {
return data.begin();
}
private:
std::vector<T> data;
};
And the overloaded operators would be written like :
template <typename T>
void operator -= (Vector<T>& a, const Vector<T>& b) {
if (a.size() != b.size()) {
throw std::length_error("void operator -= (Vector<T>& a, const Vector<T>& b)\nBoth Vector<T> operands should have the same size");
}
for (int i = 0; i < a.size(); ++i) {
a[i] -= b[i];
}
}
My question is the following : how viable is this strategy ? Is there something inherently wrong with that ?
I do feel that there is quite a big amount of boilerplate code (all functions with arguments and with return value will look in a certain way, and only the name of the functions that will be passed to the data member will change), do you see a potential improvement possible there ?
The simplest and easiest way to achieve what you want is to implement a free function :
template <class T>
void minusEqual(std::vector<T>& target, const std::vector<T>& param)
{
if (a.size() != b.size()) {
throw std::length_error("void operator -= (Vector<T>& a, const Vector<T>& b)\nBoth Vector<T> operands should have the same size");
}
for (int i = 0; i < a.size(); ++i) {
a[i] -= b[i];
}
}
Of course, in order to prevent other contributors from duplicating it, you need to store it in a clear way so that they will know where to find it.
Although it is usually discouraged, you can also inherit from std::vector. This question and its answer will give you useful information about that.
(also see Is there a good way not to hand-write all twelve required Container functions for a custom type in C++? )
For a class such as
namespace JDanielSmith {
class C
{
const size_t _size;
const std::unique_ptr<int[]> _data;
public:
C(size_t size) : _size(size), _data(new int[size]) {}
inline const int* get() const noexcept { return _data.get(); }
inline int* get() noexcept { return _data.get(); }
size_t size() const noexcept { return _size; }
};
}
what is the preferred way to expose iteration? Should I write begin()/end() (and cbegin()/cend()) member functions?
const int* cbegin() const {
return get();
}
const int* cend() const {
return cbegin() + size();
}
or should these be non-member functions?
const int* cbegin(const C& c) {
return c.get();
}
const int* cend(const C& c) {
return cbegin(c) + c.size();
}
Should begin()/end() have both const and non-const overloads?
const int* begin() const {
return get();
}
int* begin() {
return get();
}
Are there any other things to consider? Are there tools/techniques to make this "easy to get right" and reduce the amount of boiler-plate code?
Some related questions/discussion include:
Should custom containers have free begin/end functions?
Why use non-member begin and end functions in C++11?
When to use std::begin and std::end instead of container specific versions
There is a standard which describes what your class interfaces should look like if you want them to be congruent with the STL. C++ has this notion of 'concepts' which pin down the requirements for a given class to be a sufficient implementation of a concept. This almost became a language feature in c++11.
A concept you may be interested in is the Container concept. As you can see, in order to meet the requirements of the Container concept, you need begin, cbegin, end, and cend as member functions (among other things).
Since it looks like you're storing your data in an array, you might also be interested in SequenceContainer.
I'll take option C.
The main problem here is that std::begin() doesn't actually work for finding non-member begin() with ADL. So the real solution is to write your own that does:
namespace details {
using std::begin;
template <class C>
constexpr auto adl_begin(C& c) noexcept(noexcept(begin(c)))
-> decltype(begin(c))
{
return begin(c);
}
}
using details::adl_begin;
Now, it doesn't matter if you write your begin() as a member or non-member function, just use adl_begin(x) everywhere and it'll just work. As well as for both the standard containers and raw arrays. This conveniently side-steps the member vs. non-member discussion.
And yes, you should have const and non-const overloads of begin() and friends, if you want to expose const and non-const access.
I suggest creating both sets of functions -- member functions as well as non-member functions -- to allow for maximum flexibility.
namespace JDanielSmith {
class C
{
const size_t _size;
const std::unique_ptr<int[]> _data;
public:
C(size_t size) : _size(size), _data(new int[size]) {}
inline const int* get() const { return _data.get(); }
inline int* get() { return _data.get(); }
size_t size() const { return _size; }
int* begin() { return get(); }
int* end() { return get() + _size; }
const int* begin() const { return get(); }
const int* end() const { return get() + _size; }
const int* cbegin() const { return get(); }
const int* cend() const { return get() + _size; }
};
int* begin(C& c) { return c.begin(); }
int* end(C& c) { return c.end(); }
const int* begin(C const& c) { return c.begin(); }
const int* end(C const& c) { return c.end(); }
const int* cbegin(C const& c) { return c.begin(); }
const int* cend(C const& c) { return c.end(); }
}
The member functions are necessary if you want to be able to use objects of type C as arguments to std::begin, std::end, std::cbegin, and std::cend.
The non-member functions are necessary if you want to be able to use objects of type C as arguments to just begin, end, cbegin, and cend. ADL will make sure that the non-member functions will be found for such usages.
int main()
{
JDanielSmith::C c1(10);
{
// Non-const member functions are found
auto b = std::begin(c1);
auto e = std::end(c1);
for (int i = 0; b != e; ++b, ++i )
{
*b = i*10;
}
}
JDanielSmith::C const& c2 = c1;
{
// Const member functions are found
auto b = std::begin(c2);
auto e = std::end(c2);
for ( ; b != e; ++b )
{
std::cout << *b << std::endl;
}
}
{
// Non-member functions with const-objects as argument are found
auto b = begin(c2);
auto e = end(c2);
for ( ; b != e; ++b )
{
std::cout << *b << std::endl;
}
}
}
In order to create a valid iterator, you must ensure that std::iterator_traits is valid. This means you must set the iterator category among other things.
An iterator should implement iterator(), iterator(iterator&&), iterator(iterator const&), operator==, operator !=, operator++, operator++(int), operator*, operator=, and operator->. It's also a good idea to add operator< and operator+ if you can (you can't always, e.g. linked lists.)
template <typename T>
class foo
{
public:
using value_type = T;
class iterator
{
public:
using value_type = foo::value_type;
using iterator_category = std::random_access_iterator_tag;
// or whatever type of iterator you have...
using pointer = value_type*;
using reference = value_type&;
using difference_type = std::ptrdiff_t;
// ...
};
class const_iterator
{
// ...
};
iterator begin() { /*...*/ }
iterator end() { /*...*/ }
const_iterator cbegin() const { /*...*/ }
const_iterator cend() const { /*...*/ }
/* ... */
};
See: http://en.cppreference.com/w/cpp/iterator/iterator_traits for more information on what you need to make a valid iterator. (Note: You also need certain properties to be a valid "container", like .size())
Ideally you should use member functions for begin and end, but it's not required... you can also overload std::begin and std::end. If you don't know how to do that, I suggest you use member functions.
You should create begin() const and end() const, but it should be an alias for cbegin(), NEVER the same as begin()!
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).
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.
Have a quick question about what would be the best way to implement iterators in the following:
Say I have a templated base class 'List' and two subclasses "ListImpl1" and "ListImpl2". The basic requirement of the base class is to be iterable i.e. I can do:
for(List<T>::iterator it = list->begin(); it != list->end(); it++){
...
}
I also want to allow iterator addition e.g.:
for(List<T>::iterator it = list->begin()+5; it != list->end(); it++){
...
}
So the problem is that the implementation of the iterator for ListImpl1 will be different to that for ListImpl2. I got around this by using a wrapper ListIterator containing a pointer to a ListIteratorImpl with subclasses ListIteratorImpl2 and ListIteratorImpl2, but it's all getting pretty messy, especially when you need to implement operator+ in the ListIterator.
Any thoughts on a better design to get around these issues?
If you can get away with making List<T>::iterator non-virtual, then delegating the virtualness off add to List makes things simple:
template<typename T>
class List
{
virtual void add_assign(iterator& left, int right) = 0;
public:
class iterator
{
const List* list;
const T* item;
public:
iterator(const List* list, const T* item) : list(list), item(item) {}
iterator& operator +=(int right)
{
list->add_assign(*this, right);
return *this;
}
static iterator operator +(iterator const& left, int right)
{
iterator result = left;
result += right;
return result;
}
};
virtual iterator begin() const = 0;
virtual iterator end() const = 0;
};
Otherwise (if the iterators need to store significantly different data, for example), then you have to do the regular, boring pointer-to-implementation to get your virtualness:
template<typename T>
class List
{
class ItImpl
{
virtual ItImpl* clone() = 0;
virtual void increment() = 0;
virtual void add(int right) = 0;
};
public:
class iterator
{
ItImpl* impl;
public:
// Boring memory management stuff.
iterator() : impl() {}
iterator(ItImpl* impl) : impl(impl) {}
iterator(iterator const& right) : impl(right.impl->clone()) {}
~iterator() { delete impl; }
iterator& operator=(iterator const& right)
{
delete impl;
impl = right.impl->clone();
return *this;
}
// forward operators to virtual calls through impl.
iterator& operator+=(int right)
{
impl->add(right);
return *this;
}
iterator& operator++()
{
impl->increment();
return *this;
}
};
};
template<typename T>
static List<T>::iterator operator+(List<T>::iterator const& left, int right)
{
List<T>::iterator result = left;
result += right;
return result;
}
template<typename T>
class MagicList : public List<T>
{
class MagicItImpl : public ItImpl
{
const MagicList* list;
const magic* the_magic;
// implement ...
};
public:
iterator begin() const { return iterator(new MagicItImpl(this, begin_magic)); }
iterator end() const { return iterator(new MagicItImpl(this, end_magic)); }
};
There is something very important among iterators, called Iterator Category:
InputIterator
OutputIterator
ForwardIterator
BidirectionalIterator
RandomAccessIterator
Each category define an exact set of operations that are supported, efficiently, by the iterator.
Here, it seems you wish to turn down that powerful identification mechanism to create some kind of bastard category in which the operations are all present, but no guarantee is made on their efficiency.
I think your design smells.
So the problem is that the
implementation of the iterator for
ListImpl1 will be different to that
for ListImpl2. I got around this by
using a wrapper ListIterator
containing a pointer to a
ListIteratorImpl with subclasses
ListIteratorImpl2 and
ListIteratorImpl2, but it's all
getting pretty messy, especially when
you need to implement operator+ in the
ListIterator.
This design is fine IMHO, I can't see anything messy about it. Except for equality and subtraction, operations of the iterator can be implemented by virtual function pretty easily, so you'll have something like
class ListIteratorInterface // abstract
{
protected:
virtual Data& operator*()=0;
// and other operations
};
class ListIteratorA;
class ListIteratorB; // implementation of the above
class ListIterator
{
ListIteratorInterface* impl_;
public:
// when you create this, allocate impl_ on the heap
// operations, forward anything to impl_
};
You could store operator+ as a private virtual method in the base class, and have the iterator call that.
Alternatively, you could consider statically polymorphic list classes, rather than runtime polymorphism.
Say I have a templated base class 'List' and two subclasses "ListImpl1" and "ListImpl2"
What exactly do you gain by using inheritance here?