Last active
August 29, 2015 13:56
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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'; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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) ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 ) ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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