There are so many alternative ways of addressing elements of a vector.
I could use a pointer like so:
vector<int> v = {10, 11, 12};
int *p = &v[0];
cout << *p; //Outputs "10"
I could use a pointer this way too:
vector<int> v = {10, 11, 12};
vector<int>::pointer p = v.data();
cout << *p; //Outputs "10"
I could also use the iterator type:
vector<int> v = {10, 11, 12};
vector<int>::iterator i = v.begin();
cout << *i; //Outputs "10"
Are there any significant differences that I'm missing here?
As far as being able to perform the task at hand, they all work equally well. After all, they all provide an object which meets the requirements of an iterator and you are using them to point at the same element of the vector. However, I would pick the vector<int>::iterator option because the type is more expressive about how we intend to use it.
The raw pointer type, int*, tells you very little about what p is, except that it stores the address of an int. If you think about p in isolation, its type doesn't tell you very much about how you can use it. The vector<int>::pointer option has the same issue - it just expresses the type of the objects it points at as being the element type of a vector. There's no reason it actually needs to point into a vector.
On the other hand vector<int>::iterator tells you everything you need to know. It explicitly states that the object is an iterator and that iterator is used to point at elements in a vector<int>.
This also has the benefit of being more easily maintainable if you ever happen to change the container type. If you changed to a std::list, for example, the pointer type just wouldn't work any more because the elements are not stored as a contiguous array. The iterator type of a container always provides you with a type you can use to iterate over its elements.
When we have Concepts, I'd expect the best practise to be something like:
ForwardIteratorOf<int> it = std::begin(v);
where ForwardIteratorOf<int> (which I am imagining exists) is changed to whatever concept best describes your intentions for it. If the type of the elements doesn't matter, then just ForwardIterator (or BidirectionalIterator, RandomAccessIterator, or whatever).
If you add the check:
if ( !v.empty() )
Then, all the example you've shown are equally valid.
If you are about to iterate over the elements of the vector, I would go with:
vector<int>::iterator i = v.begin();
It's easier to check whether the iterator has reached the end of the vector with an iterator than with the other forms.
if ( i != v.end() )
{
// Do stuff.
}
All these ways have their advantages, but at the core they are very similar. Some of them don't work though (they cause so-called "undefined behaviour") when the vector is empty.
According to cppreference:
A pointer to an element of an array satisfies all requirements of LegacyContiguousIterator
which is the most powerful iterator as it encompasses all other iterators functionality. So they can be one and the same, an iterator is just a means of making our code clear, consice and portable.
For example we could have some container "C"...
//template <typename T, int N> class C { //for static allocation
template <typename T> class C {
//T _data[N]; //for static allocation
T* _data; //need to dynamically allocate _data
public:
typedef T* iterator;
}
where C<int>::iterator would be an int* and there would be no difference.
Maybe we don't want/need the full power of a LegacyContiguousIterator so we could redefine C<int>::iterator
as another class that follows the outline for say LegacyForwardIterator. This new iterator class may redefine operator*. In this case it is implementation dependant and an int* may cause undefined behaviour when trying to access the elements.
This is why iterators should be preferred but in most cases they are going to be the same thing.
In both cases our container " C" will work just like other STL containers so long as we define all the other necessary member functions and typedefs.
Related
I would like to know if it is possible to obtain an iterator to an object inside a container (e.g. std::vector<...>) by only having access to the object inside the container, e.g. through a reference (which implies we have access to a pointer to it using the & operator). For example, normally we declare an iterator as
std::vector<int>::iterator = vec.begin();
or
std::vector<int>::iterator = next(vec.begin(), idx);
but in the first example we are most probably about to iterate through the container, in order, while in the second example we know the index of the object we require. I would like to know if we can obtain the iterator to an object without knowing at which index it resides in the container, but if we do have a reference or a pointer to it, as explained above.
It might appear that this question has already been asked here, but it seems more like the OP wanted others to fix his code, rather than answering the general question, so the answers are not so satisfactory in my opinion. Also, the answer here seems to say that we can initialize an iterator with a constructor, as shown below
std::vector<int>::iterator it(...);
but I have not been able to find any evidence of a constructor for the std::iterator class in the official documentation (and neither have I been able to find any documentation on std::vector<...>::iterator) so I am wary to use the constructor shown above, even if it compiles.
NOTE
I use std::vector as an example above, but ideally I would like this to work for any container, e.g. std::list or std::deque
Specifically for std::vector (and other contiguous containers like std::string), given a pointer to an object in the vector p, we can simply do:
auto iter = v.begin() + std::distance(v.data(), p);
This is guaranteed by the contiguity contract. Note that random access is insufficient here, the above will not work for std::deque.
For any other container, there's no easy way of doing this. You'd have to just use find_if:
auto iter = std::find_if(c.begin(), c.end(), [p](auto const& o) { return &o == p; });
For intrusive containers, the iterator will be encoded into the object itself somehow so there will be some direct mechanism for converting p to an iterator. But that will be dependent on the intrusive container itself.
You can use the find function---it returns an iterator---, supported on (almost?) all containers, to find your objects. If there are several objects which are equal under the operator==, iterate until the one with the same address has been found.
Since C++11 you can use the keyword auto to deduce the type, it makes writing the type easier.
If we know the index we can get an iterator to it bybegin() + index.
And if we don't know the index we could use the std::distance() from begin and the other iterator, which will give us an iterator to the same element.
// if we know the index
auto it1 = begin(vec) + 4;
cout << *it1;
// if we don't know the index
auto p = begin(vec) + 3;
auto it2 = begin(vec) + distance(begin(vec), p+1);
cout << *it2;
I would like to know if it is possible to obtain an iterator to an object inside a container (e.g. std::vector<...>) by only having access to the object inside the container, e.g. through a reference (which implies we have access to a pointer to it using the & operator). For example, normally we declare an iterator as
std::vector<int>::iterator = vec.begin();
or
std::vector<int>::iterator = next(vec.begin(), idx);
but in the first example we are most probably about to iterate through the container, in order, while in the second example we know the index of the object we require. I would like to know if we can obtain the iterator to an object without knowing at which index it resides in the container, but if we do have a reference or a pointer to it, as explained above.
It might appear that this question has already been asked here, but it seems more like the OP wanted others to fix his code, rather than answering the general question, so the answers are not so satisfactory in my opinion. Also, the answer here seems to say that we can initialize an iterator with a constructor, as shown below
std::vector<int>::iterator it(...);
but I have not been able to find any evidence of a constructor for the std::iterator class in the official documentation (and neither have I been able to find any documentation on std::vector<...>::iterator) so I am wary to use the constructor shown above, even if it compiles.
NOTE
I use std::vector as an example above, but ideally I would like this to work for any container, e.g. std::list or std::deque
Specifically for std::vector (and other contiguous containers like std::string), given a pointer to an object in the vector p, we can simply do:
auto iter = v.begin() + std::distance(v.data(), p);
This is guaranteed by the contiguity contract. Note that random access is insufficient here, the above will not work for std::deque.
For any other container, there's no easy way of doing this. You'd have to just use find_if:
auto iter = std::find_if(c.begin(), c.end(), [p](auto const& o) { return &o == p; });
For intrusive containers, the iterator will be encoded into the object itself somehow so there will be some direct mechanism for converting p to an iterator. But that will be dependent on the intrusive container itself.
You can use the find function---it returns an iterator---, supported on (almost?) all containers, to find your objects. If there are several objects which are equal under the operator==, iterate until the one with the same address has been found.
Since C++11 you can use the keyword auto to deduce the type, it makes writing the type easier.
If we know the index we can get an iterator to it bybegin() + index.
And if we don't know the index we could use the std::distance() from begin and the other iterator, which will give us an iterator to the same element.
// if we know the index
auto it1 = begin(vec) + 4;
cout << *it1;
// if we don't know the index
auto p = begin(vec) + 3;
auto it2 = begin(vec) + distance(begin(vec), p+1);
cout << *it2;
For example, the following is possible:
std::set<int> s;
std::set<int>::iterator it = s.begin();
I wonder if the opposite is possible, say,
std::set<int>* pSet = it->**getContainer**(); // something like this...
No, there is no portable way to do this.
An iterator may not even have a reference to the container. For example, an implementation could use T* as the iterator type for both std::array<T, N> and std::vector<T>, since both store their elements as arrays.
In addition, iterators are far more general than containers, and not all iterators point into containers (for example, there are input and output iterators that read to and write from streams).
No. You must remember the container that an iterator came from, at the time that you find the iterator.
A possible reason for this restriction is that pointers were meant to be valid iterators and there's no way to ask a pointer to figure out where it came from (e.g. if you point 4 elements into an array, how from that pointer alone can you tell where the beginning of the array is?).
It is possible with at least one of the std iterators and some trickery.
The std::back_insert_iterator needs a pointer to the container to call its push_back method. Moreover this pointer is protected only.
#include <iterator>
template <typename Container>
struct get_a_pointer_iterator : std::back_insert_iterator<Container> {
typedef std::back_insert_iterator<Container> base;
get_a_pointer_iterator(Container& c) : base(c) {}
Container* getPointer(){ return base::container;}
};
#include <iostream>
int main() {
std::vector<int> x{1};
auto p = get_a_pointer_iterator<std::vector<int>>(x);
std::cout << (*p.getPointer()).at(0);
}
This is of course of no pratical use, but merely an example of an std iterator that indeed carries a pointer to its container, though a quite special one (eg. incrementing a std::back_insert_iterator is a noop). The whole point of using iterators is not to know where the elements are coming from. On the other hand, if you ever wanted an iterator that lets you get a pointer to the container, you could write one.
While iterating through a std::map or std::vector or any container which has iterator in it, is checked against the variable.end() and not something like container<>::end. For example,
map<int, int> var;
for(map<int, int>::iterator it = var.begin(); it != var.end(); it++)
... ^^^^^^^^^^^^^^^
Can't above highlighted part be something like:
it != map<int,int>::end
which is similar to static member string::npos. What can be the reason behind the design decision for providing .end() on per variable bases and not on the per type of container bases ? (i.e. map<int,int>::end and map<int,double>::end would be different; but for every map<int,int> variable, the ::end will be similar.)
This way, some array-based containers could implement an iterator simply as a pointer to an element. The one-past-the-last-element pointer is different for every array.
Consider the following two arrays:
int a1[] = {2, 3, 4};
int a2[] = {5, 6, 7};
One past the last element of the 2 arrays are different:
int *p1 = a1 + 3; // for a1
int *p2 = a2 + 3; // for a2
You can't use one pointer for the other. There is no universal constant to represent end-of-array. The following is wrong:
for (int *p = a1; p != a2; p++)
The explanation of not having same end() for same kind of container's iterator is similar.
Iterators are for iterating, moving forward and backward. They need to provide increment and decrement (bidirectional iterators) operations. When you decrement an end() iterator, you get to the last item in the container. This allows iterating backwards.
As to your suggestion. Implementing this would mean that each iterator carries the end iterator with it, and compares equal to the static end iterator when it is equal to the real end iterator it is storing. There would actually be no point to make all the static end iterators different types, because that "iterator" cannot be used for anything other than that anyway.
A design like that would simply be more cumbersome to implement and more wasteful to use. If each iterator has to know the end of its container, then to designate an arbitrary range, you'd have to add two potentially unused iterators to the end in the mix.
A const int * and an int *const are very different. Similarly with const std::auto_ptr<int> vs. std::auto_ptr<const int>. However, there appears to be no such distinction with const std::vector<int> vs. std::vector<const int> (actually I'm not sure the second is even allowed). Why is this?
Sometimes I have a function which I want to pass a reference to a vector. The function shouldn't modify the vector itself (eg. no push_back()), but it wants to modify each of the contained values (say, increment them). Similarly, I might want a function to only change the vector structure but not modify any of its existing contents (though this would be odd). This kind of thing is possible with std::auto_ptr (for example), but because std::vector::front() (for example) is defined as
const T &front() const;
T &front();
rather than just
T &front() const;
There's no way to express this.
Examples of what I want to do:
//create a (non-modifiable) auto_ptr containing a (modifiable) int
const std::auto_ptr<int> a(new int(3));
//this works and makes sense - changing the value pointed to, not the pointer itself
*a = 4;
//this is an error, as it should be
a.reset();
//create a (non-modifiable) vector containing a (modifiable) int
const std::vector<int> v(1, 3);
//this makes sense to me but doesn't work - trying to change the value in the vector, not the vector itself
v.front() = 4;
//this is an error, as it should be
v.clear();
It's a design decision.
If you have a const container, it usually stands to reason that you don't want anybody to modify the elements that it contains, which are an intrinsic part of it. That the container completely "owns" these elements "solidifies the bond", if you will.
This is in contrast to the historic, more lower-level "container" implementations (i.e. raw arrays) which are more hands-off. As you quite rightly say, there is a big difference between int const* and int * const. But standard containers simply choose to pass the constness on.
The difference is that pointers to int do not own the ints that they point to, whereas a vector<int> does own the contained ints. A vector<int> can be conceptualised as a struct with int members, where the number of members just happens to be variable.
If you want to create a function that can modify the values contained in the vector but not the vector itself then you should design the function to accept iterator arguments.
Example:
void setAllToOne(std::vector<int>::iterator begin, std::vector<int>::iterator end)
{
std::for_each(begin, end, [](int& elem) { elem = 1; });
}
If you can afford to put the desired functionality in a header, then it can be made generic as:
template<typename OutputIterator>
void setAllToOne(OutputIterator begin, OutputIterator end)
{
typedef typename iterator_traits<OutputIterator>::reference ref;
std::for_each(begin, end, [](ref elem) { elem = 1; });
}
One big problem syntactically with what you suggest is this: a std::vector<const T> is not the same type as a std::vector<T>. Therefore, you could not pass a vector<T> to a function that expects a vector<const T> without some kind of conversion. Not a simple cast, but the creation of a new vector<const T>. And that new one could not simply share data with the old; it would have to either copy or move the data from the old one to the new one.
You can get away with this with std::shared_ptr, but that's because those are shared pointers. You can have two objects that reference the same pointer, so the conversion from a std::shared_ptr<T> to shared_ptr<const T> doesn't hurt (beyond bumping the reference count). There is no such thing as a shared_vector.
std::unique_ptr works too because they can only be moved from, not copied. Therefore, only one of them will ever have the pointer.
So what you're asking for is simply not possible.
You are correct, it is not possible to have a vector of const int primarily because the elements will not assignable (requirements for the type of the element contained in the vector).
If you want a function that only modifies the elements of a vector but not add elements to the vector itself, this is primarily what STL does for you -- have functions that are agnostic about which container a sequence of elements is contained in. The function simply takes a pair of iterators and does its thing for that sequence, completely oblivious to the fact that they are contained in a vector.
Look up "insert iterators" for getting to know about how to insert something into a container without needing to know what the elements are. E.g., back_inserter takes a container and all that it cares for is to know that the container has a member function called "push_back".