Link to home
Start Free TrialLog in
Avatar of asmyatt
asmyatt

asked on

Modifing iterator->first, but compiler says it's read only

I am porting some C++ code from windows to Solaris. The code below compiles and works in windows, but does not compile on the solaris machine.

I need to be able to set .first to -1, but it says that it is a read only structure. I'm not using a const_iterator so I don't see why this is happening.


set< pair< long,set<wstring> > >::iterator itSet2;
long &tempLong = (*itSet2).first;
tempLong = -1;

Open in new window

Avatar of evilrix
evilrix
Flag of United Kingdom of Great Britain and Northern Ireland image

In that bit of code you create an iterator but never assign a value to it, have you tried assigning it a value from a container?
Avatar of asmyatt
asmyatt

ASKER

I cut out a lot of the code. The iterator is used to iterate through this structure via a for loop :
set< pair< long, set<wstring> > > setFS;

setFS has been loaded with data at the point I try and iterate through it.
So what happens if you try something like below? The same I presume?
set< pair< long,set<wstring> > >::iterator itSet2; // = some set iterator
itSet2->first = -1;

Open in new window

Avatar of asmyatt

ASKER

yes i get the same error.
Hmmm. Well, I certainly would not discount the possibility of a compiler bug... since this is, ostensibly, perfectly valid code.
Ok, last try from me...
	set< pair< long,set<wstring> > >::iterator itSet2; // = some set iterator
	pair< long,set<wstring> > & pr = *itSet2;
	pr.first = -1;

Open in new window

Avatar of asmyatt

ASKER

I get a different error now.

error: invalid initialization of reference of type 'std::pair<long int, std::set<std::wstring, std::less<std::wstring>, std::allocator<std::wstring> > >&' from expression of type 'const std::pair<long int, std::set<std::wstring, std::less<std::wstring>, std::allocator<std::wstring> > >'

i don't see why it thinks the iterator is a const.
>>i don't see why it thinks the iterator is a const.

It shouldn't be. Yet you could try to

pair< long,set<wstring> > & pr = const_cast<pair< long,set<wstring> > > (*itSet2);
Avatar of asmyatt

ASKER

making that change gives me these errors.

error: invalid use of const_cast with type `std::pair<long int, std::set<std::wstring, std::less<std::wstring>, std::allocator<std::wstring> > >', which is not a pointer, reference, nor a pointer-to-data-member type

error: invalid initialization of non-const reference of type 'std::pair<long int, std::set<std::wstring, std::less<std::wstring>, std::allocator<std::wstring> > >&' from a temporary of type 'std::pair<long int, std::set<std::wstring, std::less<std::wstring>, std::allocator<std::wstring> > >'
Like I said above, at this point I'd strongly consider it a compiler bug... maybe log a support call with Sun.
>> Like I said above, at this point I'd strongly consider it a compiler bug...

Not really ... How do you expect to be able to change the key of a set element ? You have to erase it from the set, and re-insert it with the new value.

Did you try to compile it, evilrix ? Try this for example :

        std::set<int> mySet;
        mySet.insert(5);
        std::set<int>::iterator myIt = mySet.begin();
        *myIt = 3;                                                             // <--- OUCH

If you agree that this isn't possible, then you can easily see that the code posted in the question does pretty much the same thing : it tries to modify the key of a set element.
>> Did you try to compile it, evilrix ?
It works for me! Below is the same, right?
#include <set>
 
typedef std::set<int> set_t;
 
int main()
{
	set_t my_set;
	my_set.insert(1);
 
	set_t::iterator itr = my_set.begin();
	*itr = 2;
 
	return 0;
}

Open in new window

...I can only assume it is a compiler specific thing since it works fine in Windoze but I just tried it in gcc and that too errors. Odd (or maybe not :) ).
Yes, that's the same. Weird. What happens if you do this :

    std::set<int> mySet;
    mySet.insert(3);
    mySet.insert(5);
    std::set<int>::iterator myIt = mySet.find(5);
    *myIt = 3;

which clearly invalidates the set, since the elements are no longer unique.
Um, that's not good! I think we've just found a compiler defect in MSVC!

-            my_set      [2](2,2)      std::set<int,std::less<int>,std::allocator<int> >
            [0]      2      int
            [1]      2      int

This how the standard defines the iterators...
namespace std {
   template <class Key, class Compare = less<Key>,
   class Allocator = allocator<Key> >
   class set {
   public:
   ...
   typedef implementation defined iterator; // See 23.1
   typedef implementation defined const_iterator; // See 23.1
   typedef implementation defined size_type; // See 23.1
   typedef implementation defined difference_type;// See 23.1
   ...
};

I can see nothing in 23.1 to indicate anything specific about how these iterators should or should not behave.

Not really sure what else to say about this unless someone can find something specific in the Standard to wrap this one one way or another :)

-Rx.




#include <set>
 
typedef std::set<int> set_t;
 
int main()
{
	set_t my_set;
	my_set.insert(1);
	my_set.insert(2);
 
	set_t::iterator itr = my_set.begin();
	*itr = 2; //<--- my_set	[2](2,2)	std::set<int,std::less<int>,std::allocator<int> >
 
	return 0;
}

Open in new window

The thing is an iterator is an object that just happens to have pointer semantics so there is no reason why the iterator couldn't have a reference to its parent container so as to validate this kind of assignment is valid (I guess). This being the case and unless someone can find something specific in the standard (if anyone can I8 can!) IMO both implementations (assuming the MS way did it properly) would be valid. A none const iterator should, semantically, be modifiable otherwise why bother to implement one? Um, I think I'll shut up now before I talk myself into a corner :)

-Rx.
I didn't find anything specific about this in the standard either. I assume that gcc solved this "issue" by not allowing changing the keys in unique associative containers. MSVC apparently didn't impose that limitation.

Since I haven't found anything to suggest otherwise, I'd say that both compilers follow the standard, but one does it a bit more intelligently ...
>> A none const iterator should, semantically, be modifiable otherwise why bother to implement one?

Right, but in this case, it allows to invalidate a container ... which is worse imo than the gcc alternative which is more limiting.
ASKER CERTIFIED SOLUTION
Avatar of Infinity08
Infinity08
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>> Right, but in this case, it allows to invalidate a container ... which is worse imo than the gcc alternative which is more limiting.
No I completely agree -- I was referring to the standard being a bit wonky here and not the way gcc implements it :)
I'm pretty sure the standard is intentionally ambiguous here so as to allow iterators to be implemented is a way the is preferable for the platform. Whilst they could just be pointers they aren't specifically guaranteed to be. They are, after all, just a concept that each iterator instance is supposed to model.

BTW: I tested this on both VS2005 and VS2008 and both implement this in the same way. Like I said, if they'd bothered to trap this in the iterator and, for example, throw if the assignment invalidated the set I'd say good for MS, job well done. As it is I think a thumbs down is in order!

So as a response to the original question, I guess this will require a specific erase & insert to be completely cross platform.

-Rx.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I knew it had to be a known issue ... It's too obvious to miss heh.
BTW: The link above explains why gcc and MS implementations differ and pretty much clarifies everything we've discussed in this thread (I hope).

"Some library implementers try to address the problem by not providing mutable set iterators in the first place. This is safe, but comes at the cost of major restrictions on the usability of the set container. For this reason, other implementers decide to trust the user, and they provide mutable set iterators. The net effect is that there are different implementations of the set container and its iterators available, so we must keep an eye on portability issues."
>> I knew it had to be a known issue ... It's too obvious to miss heh.
I still don't see why this couldn't be done safely. Writing an iterator that validates a modification isn't exactly beyond the wit of man. I suppose it comes down to the normal C/C++ thing of if it'll cost time/effort to check it don't... either dis-allow or let the user burn themselves if they wish. Anyway, at least we got to the bottom of it and, for once, we were all right (or all wrong depending upon how you want to look at it I guess :-> ). I have to be honest, I've never tried (nor needed) to do this (as far as I can remember) so I have never really given it much consideration before.

@asmyatt, does that clarify what's going on for you? I'm not sure there's much else we can say about this really. The answer is, it's not safe to do what you are trying to do but some compilers will allow it and some won't. Since Solaris compilers won't allow it you have little choice but to do what I8 and I suggest above.

-Rx.
Avatar of asmyatt

ASKER

Yea I'm glad I asked...I understand now.

Thanks Guys.
I like this kind of questions ... Everybody learns something ... Thanks for asking, asmyatt ;)
>> Everybody learns something
I learn something new every day -- often from you I8 :)

Thanks all.

-Rx.