Questions about an array template in C++ - c++

I'm taking a course where we're being introduced to a few software patterns, one of them being the iterator pattern, and we're being asked to implement it in C++. Our professor gave us some sample code a few weeks ago and I'd like to understand it a little better than I do. First I'll post the code:
template <class T_,std::size_t SIZE_>
class carray {
public:
typedef T_ value_type;
typedef std::size_t size_type;
typedef T_ & reference;
typedef T_ const & const_reference;
typedef T_ * pointer;
typedef T_ const * const_pointer;
typedef T_ * iterator;
typedef T_ const * const_iterator;
typedef std::ptrdiff_t difference_type;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
// data store
value_type data[SIZE_];
size_type size() const { return SIZE_; }
reference operator [] ( size_type idx ) { return data[idx]; }
const_reference operator [] ( size_type idx ) const { return data[idx]; }
reverse_iterator rbegin() { return reverse_iterator( end() ); }
reverse_iterator rend() { return reverse_iterator( begin() ); }
const_reverse_iterator rbegin() const { return const_reverse_iterator( end() ); }
const_reverse_iterator rend() const { return const_reverse_iterator( begin() ); }
const_reverse_iterator crbegin() const { return const_reverse_iterator( end() ); }
const_reverse_iterator crend() const { return const_reverse_iterator( begin() ); }
iterator begin() { return data; }
iterator end() { return data + SIZE_; }
const_iterator begin() const { return data; }
const_iterator end() const { return data + SIZE_; }
const_iterator cbegin() const { return data; }
const_iterator cend() const { return data + SIZE_; }
};
In this template our prof defined a bunch of typedefs, and a few of them had both a non-constant and constant version. First, I wonder what the purpose of each of the public members are, and second, I wonder what the purpose of the constant versions are, in the context of defining a data structure template.
I also notice that some of the member functions are defined as 'const' and I wonder what the purpose of that is.

I wonder what the purpose of each of the public members are.
The class contains the following:
Type aliases: Type information that is exposed from the class for public use.
The non-const typedefs are type aliases for a non-const T.
The const typedefs are type aliases for a const T.
These typedefs are useful because the user of the class might need more information about the type. By using the typedef exposed by the class, he can obtain this. Here's a good example - Consider the situation where we have a template type and we need to access the underlying iterator type. By using the exposed type alias, we can access it:
template<class Container>
void example(Container& c)
{
typename Container::iterator begin = c.begin(),
// ^^^^^^^^^^^^^^^^^^^
end = c.end();
}
I wonder what the purpose of the constant versions are.
Sometimes the user needs to use const types. Therefore using the type information that class provides makes this simple.
I also notice that some of the member functions are defined as 'const' and I wonder what the purpose of that is.
const member functions are functions that do not modify any of the classes data members.

all the typedefs aim to facilitate the use of these types,
std::size_t std::ptrdiff_t is just unsigned int typedefs.
chech here for reverse_iterator
const varialbe defined for the return value of the const version function, which is provided for the const obj, check here
member function defined as const make sure the member variable is not changed in the function body, check here

For this, I think you should try to use this template, and you may really know why it need such public members.
while we are using this template to operate on some data structure, if we want to modify some data, we need a non-constant version. On the other side, if we want to change the value of data, we need a constant version which can help us protect data.
Constant functions can help you, while you are trying to operate on some data and you do not want to modify the member data.

Related

"lvalue required as increment operand" about C++ & stl vector's begin()

I'm trying to implement a vector(just like the one in STL).
Here is part of my code
template <class T, class alloc>
class vector
{
public:
typedef T value_type;
typedef const value_type * const_iterator;
typedef value_type * iterator;
iterator start;
iterator finish;
iterator end_of_storage;
iterator begin() {return start;}
iterator end() {return finish;}
...
}
And when I'm trying compile codes below
vector<char> characters(2, 2);
cout << *++(characters.begin()) << endl;
Here comes the error
error: lvalue required as increment operand
I'm not sure what happened, I think characters.begin() should be a lvalue. I've looked at the one in SGI STL and I couldn't find anything.
Many thanks for any advice.
This issue arises because you are using raw pointers as iterators. When you return a raw pointer (or other primitive type, such as int) from function it is not a modifiable value. However it can be easily fixed by using class iterator:
template<typename T>
struct base_iterator
{
T * m_p_value;
base_iterator &
operator ++(void)
{
++m_p_value;
return *this;
}
T &
operator *(void)
{
return *m_p_value;
}
};
// inside of vector
typedef base_iterator< value_type > iterator;
online compiler

How to implement standard iterators in class

I have classes which are usually using standard containers as underlying fields. For example, I have a class
template <typename T>
class Vec_3D
{
public:
/* ... */
std::array<T, 3> vec;
/* ... */
};
which has only one variable vec and the rest are just functions I need when working with vectors. I want to be able to use range-based for loop such as
Vec_3D<double> vec;
for (double val : vec) {/*...*/}
which should obviusly iterate over std::array<double, 3>.
How to implement iterators in my class which should in turn call iterators of std::array<T, 3>?
I started with this question and tried to define iterators in my class as
typedef std::iterator<std::random_access_iterator_tag, T, ptrdiff_t, T*, T&> iterator;
typedef std::iterator<std::random_access_iterator_tag, const T, ptrdiff_t, const T*, const T&> const_iterator;
inline iterator begin() noexcept { return vec.begin(); }
inline const_iterator cbegin() const noexcept { return vec.cbegin(); }
inline iterator end() noexcept { return vec.end(); }
inline const_iterator cend() const noexcept { return vec.end(); }
but got compiling errors
error: no match for ‘operator!=’ (operand types are ‘Vec_3D<double>::iterator {aka std::iterator<std::random_access_iterator_tag, double, long int, double*, double&>}’ and ‘Vec_3D<double>::iterator {aka std::iterator<std::random_access_iterator_tag, double, long int, double*, double&>}’)
and operator++, operator*
std::iterator is (was) a helper type to define the typedefs that a typical iterator requires. These typedefs within the class in turn make std::iterator_traits work with your iterator.
It does not, however, actually implement the required operations for you.
It was deprecated, because the std committee didn't like specifying that standard iterators had to have those typedefs, and writing the typedefs was not much bulkier than figuring out what arguments to pass to the std::iterator template.
The easy thing to do here is to just steal your underlying container's iterator. This makes your abstraction leak, but it is efficient and easy.
template <typename T>
struct Vec_3D {
using container=std::array<T, 3>;
using iterator=typename container::iterator;
using const_iterator=typename container::const_iterator;
iterator begin() { return vec.begin(); }
iterator end() { return vec.end(); }
const_iterator begin() const { return vec.begin(); }
const_iterator end() const { return vec.end(); }
private:
/* ... */
container vec;
/* ... */
};
If you don't want to expose your underlying container type, if you are willing to guarantee your underlying container is a contiguous buffer you can do:
template <typename T>
struct Vec_3D {
using iterator=T*;
using const_iterator=T const*;
iterator begin() { return vec.data(); }
iterator end() { return vec.data()+vec.size(); }
const_iterator begin() const { return vec.data(); }
const_iterator end() const { return vec.data()+vec.size(); }
private:
/* ... */
std::array<T,3> vec;
/* ... */
};
as pointers are valid iterators.
If you find you are writing this "I am a modified container" boilerplate too much, you can automate it:
template<class Container>
struct container_wrapper {
using container=Container;
using iterator=typename container::iterator;
using const_iterator=typename container::const_iterator;
iterator begin() { return m_data.begin(); }
iterator end() { return m_data.end(); }
const_iterator begin() const { return m_data.begin(); }
const_iterator end() const { return m_data.end(); }
protected:
Container m_data;
};
and then
template <typename T>
class Vec_3D:private container_wrapper<std::array<T,3>> {
// ...
};
but even that might be a bit much, why not just:
template <typename T>
class Vec_3D:public std::array<T,3> {
// ...
};
It is true that deleting Vec_3D through a pointer to base is undefined behavior, but who deletes pointers to standard containers?
If this worries you:
template <typename T>
class Vec_3D: private std::array<T,3> {
using container = std::array<T,3>;
using container::begin();
using container::end();
// ...
};
lets you inherit privately, then bring certain operations back into scope.
A range-based for loop only requires that your class have begin() and end() methods (or overloads of std::begin() and std::end()) that return iterators. It doesn't care where those iterators come from. So, the simplest solution is to just use the array's own iterators instead of trying to define your own, eg:
template <typename T>
class Vec_3D
{
public:
typedef typename std::array<T, 3> array_type;
typedef typename array_type::iterator iterator;
typedef typename array_type::const_iterator const_iterator;
// or:
// using array_type = std::array<T, 3>;
// using iterator = array_type::iterator;
// using const_iterator = array_type::const_iterator;
...
inline iterator begin() noexcept { return vec.begin(); }
inline const_iterator cbegin() const noexcept { return vec.cbegin(); }
inline iterator end() noexcept { return vec.end(); }
inline const_iterator cend() const noexcept { return vec.cend(); }
...
private:
array_type vec;
};
std::iterator is a base class only, its basically a container for some traits, but if you wanted to use it to implement your own iterator class you'd need to derive from it.
However you don't need to use it, there was a proposal to deprecate it, you could just define those traits directly in an iterator that you write. The following question has info on the proposal and help with implementing an iterator class:-
Preparation for std::iterator Being Deprecated
At the moment you're defining your container's iterator types using that base, not a class that can actually do any iterating, which is why it fails.
You expose the array as a public member. If you're happy to expose that your vec_3d is implemented using an array (whether you continue to expose the member array publicly or not) then you could just use the array's iterators - its not clear from the question that your iterator needs any bespoke behaviour just because your container adds some functionality.

When is it sufficient to declare const_iterator as a const iterator?

For example, I could define a container in a way similar to:
template <typename T>
class Example
{
public:
using value_type = T;
using iterator = value_type*;
using const_iterator = const iterator;
//etc
};
However, is it okay to do this with a user-defined iterator?
template<typename T>
class Example
{
public:
using value_type = T;
/*friend?*/ class iterator;
using const_iterator = const iterator; //is this okay?
//etc
};
A detailed explanation would be appreciated.
A const_iterator and a const iterator represent two different things. In each case, the const applies to something different: the object "pointed to" by the iterator in the first case, and the iterator itself in the second. So, it is never "sufficient" to use one for the other.
In pointer speak (and pointers are iterators), this would be the difference between "pointer to const" and "const pointer".
const int* it; // pointer to const int: a const_iterator
int* it const; // const pointer to int: a const iterator
You can even have const iterators to const, or const const_iterator:
const int* it const;

How to write a C++11 template that can take a const iterator

In responding to this question on CodeReview, I was thinking about how one might write a template function to indicate const-ness of a contained object.
To be specific, consider this templated function
#include <iostream>
#include <numeric>
#include <vector>
template <class It>
typename std::iterator_traits<It>::value_type average(It begin, It end) {
typedef typename std::iterator_traits<It>::value_type real;
real sum = real();
unsigned count = 0;
for ( ; begin != end; ++begin, ++count)
sum += *begin;
return sum/count;
}
int main()
{
std::vector<double> v(1000);
std::iota(v.begin(), v.end(), 42);
double avg = average(v.cbegin(), v.cend());
std::cout << "avg = " << avg << '\n';
}
It takes an iterator and calculates an average based on the contained numbers, but it is guaranteed not to modify the vector through the passed iterators. How does one convey this to a user of the template?
Note that declaring it like this:
template <class It>
typename std::iterator_traits<It>::value_type average(const It begin,
const It end)
doesn't work because it's not the iterator, but the thing the iterator points to, that's const. Do I have to wait for concepts to be standardized?
Note that I don't want to require const iterators, but instead to indicate that they may be safely used here. That is, rather than restricting the caller, I want to convey a promise that my code is making: "I will not modify your underlying data."
template <class ConstIt>
It's that simple. There's nothing to be enforced on the caller side here, as a non-const iterator is also usable for const access, so it's just API documentation, and that's what your choice of parameter identifier is - API documentation.
That does lead on to the question of enforcement on the callee/function side - so it can't be pretending it will only use the iterator for const access then modify elements anyway. Should you care about that, you could accept the parameter using some identifier making it clear it wasn't meant to be used everywhere throughout the function, then create a const_iterator version with a more convenient identifier. That could be tricky as in general you don't know if the iterator type is a member of a container, let alone what that container type is and whether it has a const_iterator too, so some manner of Concepts would indeed be ideal - fingers crossed for C++14. Meanwhile:
have your caller tell you the container type,
write your own traits, OR
write a simple wrapper class that holds an iterator and ensures only const access to the referenced data escapes the interface
This last wrapper approach is illustrated below (not all of the iterator API is implemented so flesh out as needed):
template <typename Iterator>
class const_iterator
{
public:
typedef Iterator iterator_type;
typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
// note: trying to add const to ...:reference or ..:pointer doesn't work,
// as it's like saying T* const rather than T const* aka const T*.
typedef const typename std::iterator_traits<Iterator>::value_type& reference;
typedef const typename std::iterator_traits<Iterator>::value_type* pointer;
const_iterator(const Iterator& i) : i_(i) { }
reference operator*() const { return *i_; }
pointer operator->() const { return i_; }
bool operator==(const const_iterator& rhs) const { return i_ == rhs.i_; }
bool operator!=(const const_iterator& rhs) const { return i_ != rhs.i_; }
const_iterator& operator++() { ++i_; return *this; }
const_iterator operator++(int) const { Iterator i = i_; ++i_; return i; }
private:
Iterator i_;
};
Sample usage:
template <typename Const_Iterator>
void f(const Const_Iterator& b__, const Const_Iterator& e__)
{
const_iterator<Const_Iterator> b{b__}, e{e__}; // make a really-const iterator
// *b = 2; // if uncommented, compile-time error....
for ( ; b != e; ++b)
std::cout << *b << '\n';
}
See it running at ideone.com here.
You may add some traits to see if the iterator is a const_iterator:
template <typename IT>
using is_const_iterator =
std::is_const<typename std::remove_reference<typename std::iterator_traits<IT>::reference>::type>;
And then use something like:
template <typename IT>
typename
std::enable_if<is_const_iterator<IT>::value,
typename std::iterator_traits<It>::value_type
>::type average(It begin, It end);
But this will avoid the use of iterator which are convertible to const_iterator.
So it will be better to restrict iterator when const is forbidden (as in std::sort)
It takes an iterator and calculates an average based on the contained numbers, but it is guaranteed not to modify the vector through the passed iterators. How does one convey this to a user of the template?
You could use SFINAE to disable the template when non-const iterators are passed, but that would be an unnecessary limitation.
Another way is to accept ranges instead of iterators. This way you could write:
template <class Range>
typename Range::value_type average(Range const& range);
The user can pass a container or iterator range in there.
You could try always dereferencing the iterator through some function deref().
template <typename It>
typename ::std::remove_reference<typename ::std::iterator_traits<It>::reference>::type const&
deref(It it)
{
return *it;
}
Which would guarantee the underlying value will not be modified.

C++11 cast const iterator pointing to container of shared_ptr objects

I have an STL container whose element type is const std::shared_ptr<MyClass>.
I want to supply two iterator types to the user:
MyContainer::iterator
typedefed as std::vector<const std::shared_ptr<MyClass>>::iterator
(which should be the same type as std::vector<const std::shared_ptr<const MyClass>>::const_iterator
MyContainer::const_iterator
typedefed as std::vector<const std::shared_ptr<const MyClass>>::iterator
(which should be the same type as std::vector<const std::shared_ptr<const MyClass>>::const_iterator
In other words, I want the "const" to refer to the MyClass constness, not shared_ptr constness. The solution I found for getting the second iterator type is getting the first one, which is easy (e.g. using vector::begin), and then converting it to the second type using static_cast (fixme: no need to use const_cast because I'm adding constness, not removing it).
Would that be the common good-design way to achieve that, or there's a better/more common way?
typedefed as std::vector<const std::shared_ptr<MyClass>>::iterator (which should be the same type as std::vector<std::shared_ptr<const MyClass>>::const_iterator
But it probably isn't the same type. Iterators are not just pointers. If the iterator and const_iterator types are defined inside vector then they are completely unrelated types:
template<typename T>
class vector
{
class iterator;
class const_iterator;
// ...
vector<const int> is a different type to vector<int> and so their nested types are also different. As far as the compiler is concerned they are completely unrelated types, i.e. you cannot just move const around to any point in this type and get compatible types:
vector<const shared_ptr<const T>>::iterator
You cannot use const_cast to convert between unrelated types. You can use static_cast to convert a vector<T>::iterator to a vector<T>::const_iterator but it's not really a cast, you're constructing the latter from the former, which is allowed because that conversion is required by the standard.
You can convert a shared_ptr<const T> to a shared_ptr<T> with const_pointer_cast<T> but again only because it's defined to work by the standard, not because the types are inherently compatible and not because it "just works" like plain ol' pointers.
Since vector's iterators don't provide the deep-constness you want, you'll need to write your own, but it's not hard:
class MyClass { };
class MyContainer
{
typedef std::vector<std::shared_ptr<MyClass>> container_type;
container_type m_cont;
public:
typedef container_type::iterator iterator;
class const_iterator
{
typedef container_type::const_iterator internal_iterator;
typedef std::iterator_traits<internal_iterator> internal_traits;
const_iterator(internal_iterator i) : m_internal(i) { }
friend class MyContainer;
public:
const_iterator() { }
const_iterator(iterator i) : m_internal(i) { }
typedef std::shared_ptr<const MyClass> value_type;
typedef const value_type& reference;
typedef const value_type* pointer;
typedef internal_traits::difference_type difference_type;
typedef internal_traits::iterator_category iterator_category;
const_iterator& operator++() { ++m_internal; return *this; }
const_iterator operator++(int) { const_iterator tmp = *this; ++m_internal; return tmp; }
reference operator*() const { m_value = *m_internal; return m_value; }
pointer operator->() const { m_value = *m_internal; return &m_value; }
// ...
private:
internal_iterator m_internal;
mutable value_type m_value;
};
iterator begin() { return m_cont.begin(); }
const_iterator begin() const { return const_iterator(m_cont.begin()); }
// ...
};
That iterator type is mising a few things (operator--, operator+) but they're easy to add, following the same ideas as already shown.
The key point to notice is that in order for const_iterator::operator* to return a reference, there needs to be a shared_ptr<const MyClass> object stored as a member of the iterator. That member acts as a "cache" for the shared_ptr<const MyClass> value, because the underlying container's real elements are a different type, shared_ptr<MyClass>, so you need somewhere to cache the converted value so a reference to it can be returned. N.B. Doing this slightly breaks the iterator requirements, because the following doesn't work as expected:
MyContainer::const_iterator ci = c.begin();
const shared_ptr<const MyClass>& ref = *ci;
const MyClass* ptr = ref.get();
++ci;
(void) *ci;
assert( ptr == ref.get() ); // FAIL!
The reason the assertion fails is that *ci doesn't return a reference to an underlying element of the container, but to a member of the iterator, which gets modified by the following increment and dereference. If this behaviour isn't acceptable you'll need to return a proxy from your iterator instead of caching a value. Or return a shared_ptr<const MyClass> when the const_iterator is dereferenced. (The difficulties of getting this 100% right is one of the reasons STL containers don't try to model deep constness!)
A lot of the effort of defining your own iterator types is done for you by the boost::iterator_adaptor utility, so the example above is only really useful for exposition. With that adaptor you'd only need to do this to get your own custom iterator types with the desired behaviour:
struct iterator
: boost::iterator_adaptor<iterator, container_type::iterator>
{
iterator() { }
iterator(container_type::iterator i) : iterator_adaptor(i) { }
};
struct const_iterator
: boost::iterator_adaptor<const_iterator, container_type::const_iterator, std::shared_ptr<const MyClass>, boost::use_default, std::shared_ptr<const MyClass>>
{
const_iterator() { }
const_iterator(iterator i) : iterator_adaptor(i.base()) { }
const_iterator(container_type::const_iterator i) : iterator_adaptor(i) { }
};
boost::iterator_adaptor makes it pretty easy to define your own iterator types based on another iterator type. So you can set it up so that *iter is a const shared_ptr<MyClass>& or const shared_ptr<const MyClass>& as desired.
Though in the const_iterator case, dereferencing can't return a const shared_ptr<const MyClass>& if what you actually have is shared_ptr<MyClass>. So we'll define const_iterator::reference as just shared_ptr<const MyClass> and return by value.
#include <boost/iterator/iterator_adaptor.hpp>
class MyContainer {
public:
class iterator;
class const_iterator;
class iterator :
public boost::iterator_adaptor<
iterator, // This class, for CRTP
std::vector<const std::shared_ptr<MyClass>>::const_iterator,
// Base type
const std::shared_ptr<MyClass> > // value_type
{
public:
iterator() {}
iterator(const iterator&) = default;
private:
friend class MyContainer; // allow private constructor
friend class boost::iterator_core_access; // allow dereference()
explicit iterator(base_type iter) : iterator_adaptor(iter) {}
const std::shared_ptr<MyClass>& dereference() const
{ return *base_reference(); }
};
class const_iterator :
public boost::iterator_adaptor<
const_iterator, // This class, for CRTP
std::vector<const std::shared_ptr<MyClass>>::const_iterator,
// Base type
const std::shared_ptr<const MyClass>, // value_type
boost::use_default, // difference_type
std::shared_ptr<const MyClass> > // reference_type
{
public:
const_iterator();
const_iterator(const const_iterator&) = default;
// Implicit conversion from iterator to const_iterator:
const_iterator(const iterator& iter) : iterator_adaptor(iter.base()) {}
private:
friend class MyContainer; // allow private constructor
friend class boost::iterator_core_access; // allow dereference()
explicit const_iterator(base_type iter) : iterator_adaptor(iter) {}
std::shared_ptr<const MyClass> dereference() const
{ return *base_reference(); }
};
iterator begin() { return iterator(mVec.begin()); }
iterator end() { return iterator(mVec.end()); }
const_iterator begin() const { return cbegin(); }
const_iterator end() const { return cend(); }
const_iterator cbegin() const { return const_iterator(mVec.begin()); }
const_iterator cend() const { return const_iterator(mVec.end()); }
private:
std::vector<const std::shared_ptr<MyClass>> mVec;
};
shared_ptr and other standard smart pointers are not designed with deep-constness in mind. They are trying to be as close to raw pointer usage as possible, and raw pointer's const-ness does not affect the pointee's const-ness.
Andrei Alexandrescu's Loki::SmartPtr (described in his Modern C++ Design) implements reference counting and deep const-ness as policies, which would give you the effect you're looking for. If you don't mind switching to a non-standard smart pointer in order to get non-standard behavior, that may be one way to go.