Skip to content

Instantly share code, notes, and snippets.

@molpopgen
Last active August 29, 2015 13:56
Show Gist options
  • Save molpopgen/9179556 to your computer and use it in GitHub Desktop.
Save molpopgen/9179556 to your computer and use it in GitHub Desktop.
Iterators != pointers, but the former are data types behaving like the latter. If iterators were pointers, they would be assignable and convertible from one another, which is not true.
/*
sizeof(iterator) and sizeof(pointer) are the same.
They also act the same vis-a-vis dereferencing.
But, they do not act the same with respect to traversal of a range
Note that this example forces iterator and pointer invalidation to occur.
Thus, the specifics of the output may vary with the optimization level
used to compile. On OS X Mavericks, -O2 provides cleaner output
than no optimization.
*/
#include <list>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
#include <iomanip>
#include <boost/format.hpp>
using namespace std;
using namespace boost;
typedef list<double> ld; //list of doubles
typedef ld::iterator ldi; //ld's iterator type
typedef vector<double *> vpd; //vec of ptr to doubles
typedef vector< ldi > vvdi; //vec of ld's iterator type iterator type
int main( int argc, char ** argv )
{
cout << "Are the sizeofs the same?"
<< sizeof(double *) << '\t' << sizeof(ldi) << '\n';
ld x;
for( unsigned i = 0 ; i < 5 ; ++i)
{
x.push_back(i);
}
for( unsigned i = 6 ; i < 10 ; ++i)
{
x.push_back(i);
}
vpd px;
vvdi ix;
for( ldi i = x.begin() ; i != x.end() ; ++i )
{
px.push_back( &*i );
ix.push_back( i );
}
cout << "Print our data. The pointers here do not increment nor decrement properly:\n";
ldi itr = ix[0];
double * ptr = px[0];
for( unsigned i = 0 ; i < ix.size() ; ++i,++itr,++ptr )
{
cout << format("%10f %10f %10f %10f") % *itr % *ix[i] % *ptr % *px[i]
<< '\n';
}
//A "new mutation" enters at position 5
x.insert(x.end(),5);
//Pointers are invalidated upon insertions, while iterators are not
//The third column will give the wrong output.
//Note that this is true even though we inserted at the end of the list!
cout << "Iterators remain valid upon insert. The pointers still do not increment and decrement properly:\n";
itr = ix[0];
ptr = px[0];
for( unsigned i = 0 ; i < ix.size() ; ++i,++itr,++ptr )
{
cout << format("%10f %10f %10f %10f") % *itr % *ix[i] % *ptr % *px[i]
<< '\n';
}
x.sort( less<double>() );
cout << "What happens after sorting using operator <:\n";
itr = ix[0];
ptr = px[0];
for( unsigned i = 0 ; i < ix.size() ; ++i,++itr,++ptr )
{
cout << format("%10f %10f %10f %10f") % *itr % *ix[i] % *ptr % *px[i];
if(i == 5)
{
cout << "\t\t\t<---note difference in first 2 columns here.\n"
<< "\t\t\t\t\t\t\t\tThis is because the iterators increment/decrement functions\n"
<< "\t\t\t\t\t\t\t\taccount for how to increment and decrement the underlying pointer.\n"
<< "\t\t\t\t\t\t\t\tThe pointers themselves do not know how to do that,\n"
<< "\t\t\t\t\t\t\t\twhich is why column three looks ugly.";
}
cout << '\n';
}
x.sort( greater<double>() );
cout << "What happens after sorting using operator >:\n"
<< "The results look confusing. What is happening is that\n"
<< "the first pointer we've stored now points to the\n"
<< "last data element, so incrementing that result in x.end()\n"
<< "Further, and curiously, further incrementing from x.end()\n"
<< "appears to return x.begin()! I suspect that to be platform-\n"
<< "depdendent.\n";
itr = ix[0];
ptr = px[0];
double * ptr2 = &*x.begin();
ldi itr2 = x.begin();
for( unsigned i = 0 ; i < ix.size() ; ++i,++itr,++itr2,++ptr,++ptr2 )
{
cout << format("%10f ") % *itr
<< (itr == x.end()) << ' '
<< (itr == x.begin()) << ' '
<< format("%10f %10f %10f %10f %10f") % *itr2 % *ix[i] % *ptr % *ptr2 % *px[i];
cout << '\n';
}
}
//A pointer is a pointer and an iterator is an iterator
//This file will compile
#include <map>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
#include <boost/call_traits.hpp>
int main(int argc, char ** argv)
{
BOOST_STATIC_ASSERT( ( boost::is_convertible< unsigned *, unsigned *>::value ) );
BOOST_STATIC_ASSERT( ( boost::is_same< boost::call_traits<unsigned *>::value_type,
boost::call_traits<unsigned *>::value_type >::value ) );
//Let's try it with something whose iterator is not a random access iterator
BOOST_STATIC_ASSERT( (boost::is_same< std::map<unsigned,unsigned>::iterator::value_type,
std::map<unsigned,unsigned>::iterator::value_type >::value) );
}
/*
If an iterator were a pointer, you'd be able to
assign an iterator from a random-access/contiguous-memory
container with the pointer to the element to which the iterator
acts as a pointer. This doesn't compile.
OS X Mavericks:
g++ -c its_dont_assign.cc
its_dont_assign.cc:11:11: error: calling a private constructor of class 'std::__1::__wrap_iter<float *>'
i = &x[j];
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/iterator:1187:31: note:
declared private here
_LIBCPP_INLINE_VISIBILITY __wrap_iter(iterator_type __x) _NOEXCEPT : __i(__x) {}
^
1 error generated.
On Ubuntu w/gcc 4.6.3:
itrs_dont_assign.cc: In function ‘int main(int, char**)’:
itrs_dont_assign.cc:30:15: error: no match for ‘operator=’ in ‘i = & x.std::vector<_Tp, _Alloc>::operator[] [with _Tp = float, _Alloc = std::allocator<float>, std::vector<_Tp, _Alloc>::reference = float&, std::vector<_Tp, _Alloc>::size_type = long unsigned int](((long unsigned int)j))’
itrs_dont_assign.cc:30:15: note: candidate is:
/usr/include/c++/4.6/bits/stl_iterator.h:702:11: note: __gnu_cxx::__normal_iterator<float*, std::vector<float> >& __gnu_cxx::__normal_iterator<float*, std::vector<float> >::operator=(const __gnu_cxx::__normal_iterator<float*, std::vector<float> >&)
/usr/include/c++/4.6/bits/stl_iterator.h:702:11: note: no known conversion for argument 1 from ‘float*’ to ‘const __gnu_cxx::__normal_iterator<float*, std::vector<float> >&’
*/
#include <vector>
int main(int argc, char ** argv)
{
std::vector<float> x(10,1.);
unsigned j = 0;
for( std::vector<float>::iterator i = x.begin();
i != x.end() ; ++i,++j )
{
i = &x[j];
}
}
//A list's iterator behaves like a pointer but is not a pointer. If they
//were the same, I'd be able to convert one to the other.
//Will fail to compile
#include <list>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
using namespace std;
int main( int argc, char ** argv )
{
BOOST_STATIC_ASSERT( ( boost::is_convertible< list<unsigned>::iterator, unsigned * >::value ) );
}
//Even for continguous-storage containers, an iterator is not convertible to a pointer,
//despite the fact that a pointer to a contiguous-storage container is
//the simplest type of iterator.
//Will fail to compile
#include <vector>
#include <iterator>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
using namespace std;
int main( int argc, char ** argv )
{
//the value type of a vector's iterator is the type stored by the vector
BOOST_STATIC_ASSERT( ( boost::is_same< unsigned,
vector<unsigned>::iterator::value_type >::value ) )
//The value type of a pointer to an unsigned and a vector of unsigned integers iterator type is also the same
BOOST_STATIC_ASSERT( ( boost::is_same< std::iterator_traits<unsigned *>::value_type,
vector<unsigned>::iterator::value_type >::value ) );
//But a pointer to that value type is not convertible to an iterator
BOOST_STATIC_ASSERT( ( boost::is_convertible< vector<unsigned>::iterator, unsigned * >::value ) );
//And it isn't the same, neither
BOOST_STATIC_ASSERT( ( boost::is_convertible< unsigned *, vector<unsigned>::iterator >::value ) );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment