g++ const 'this' problems

J

John Calcote

I'm not sure is there's a g++ specific newsgroup...

g++ is telling me there's a const-related problem with my source code
and I just don't see it. Now, I don't know that it means much, but VC7
doesn't see the problem either:

----snip-snip-snip----

#include <string>
#include <set>

using namespace std;

class X {
class Y {
set<string> _bound;
public:
void Bind(const string & x);
};

set<Y> _requests;
public:
void ApplyChanges(const set<string> & changes);
};

void X::Y::Bind(const std::string & data)
{
_bound.insert(data);
}

void X::ApplyChanges(const set<string> & changes)
{
set<Y>::iterator rip;
set<string>::const_iterator ip;
for (ip = changes.begin(); ip != changes.end(); ++ip)
for (rip = _requests.begin(); rip != _requests.end(); ++rip)
rip->Bind(*ip);
}

int main(void)
{
X x;
return 0;
}

----snip-snip-snip----

This source causes the following error messages:

test.cpp: In member function `void X::ApplyChanges(const
std::set<std::string,
std::less<std::string>, std::allocator<std::string> >&)':
test.cpp:30: passing `const X::Y' as `this' argument of `void
X::Y::Bind(const
std::string&)' discards qualifiers

Now, I can't for the life of me see how I'm violating const rules
here. I'm not passing a const 'this' pointer. I'm passing a const
parameter, true, but it's not the this pointer.

Any ideas?
John
 
G

Gianni Mariani

John said:
I'm not sure is there's a g++ specific newsgroup...

g++ is telling me there's a const-related problem with my source code
and I just don't see it. Now, I don't know that it means much, but VC7
doesn't see the problem either:

----snip-snip-snip----

#include <string>
#include <set>

using namespace std;

class X {
class Y {
set<string> _bound;
public:
void Bind(const string & x);
};

set<Y> _requests;
public:
void ApplyChanges(const set<string> & changes);
};

void X::Y::Bind(const std::string & data)
{
_bound.insert(data);
}

void X::ApplyChanges(const set<string> & changes)
{
set<Y>::iterator rip;
set<string>::const_iterator ip;
for (ip = changes.begin(); ip != changes.end(); ++ip)
for (rip = _requests.begin(); rip != _requests.end(); ++rip)
rip->Bind(*ip);

I changed that code to this:

Y * pr_rip = &(* rip);
pr_rip->Bind(*ip);

gcc_constthis.cpp:30: error: invalid conversion from `const X::Y*' to
`X::Y*'

It seems at though it is a const_iterator.

Looks like a gcc bug.

It compiles fine on VC++7.1.
 
T

tom_usenet

R

Rob Williscroft

John Calcote wrote in
I'm not sure is there's a g++ specific newsgroup...

g++ is telling me there's a const-related problem with my source code
and I just don't see it. Now, I don't know that it means much, but VC7
doesn't see the problem either:

VC7 is wrong, If there is any doubt about this it compiles this
nonsence:

#include <iostream>
#include <ostream>
#include <set>

using namespace std;

int main()
{
set< int > si;
for ( int i = 0; i < 10; ++i ) si.insert( i );

*si.find( 2 ) = 4;

set< int >::iterator ptr, lim;

for ( ptr = si.begin(), lim = si.end(); ptr != lim; ++ptr )
{
cout << *ptr << endl;
}
}

This compiles with VC 7.1 ( Dinkumware Standard library 313 ).

Anyway, your original code with some addition's/fixes:

#include <string>
#include <set>

using namespace std;

class X {
class Y {
mutable set<string> _bound;
int key;
static int static_key;

public:

void Bind(const string & x) const;
Y() : key( ++static_key ) {}
bool operator < ( Y const &rhs ) const
{
return key < rhs.key;
}
};

set<Y> _requests;
public:
void ApplyChanges(const set<string> & changes);
void add_request()
{
_requests.insert( Y() );
}
};

int X::Y::static_key = 0;

void X::Y::Bind(const std::string & data) const
{
_bound.insert(data);
}

void X::ApplyChanges(const set<string> & changes)
{
set<Y>::iterator rip;
set<string>::const_iterator ip;
for (ip = changes.begin(); ip != changes.end(); ++ip)
for (rip = _requests.begin(); rip != _requests.end(); ++rip)
rip->Bind(*ip);
}


int main(void)
{
X x;

x.add_request();

set< string > ss;

ss.insert( " a " );

x.ApplyChanges( ss );

return 0;
}
This source causes the following error messages:

test.cpp: In member function `void X::ApplyChanges(const
std::set<std::string,
std::less<std::string>, std::allocator<std::string> >&)':
test.cpp:30: passing `const X::Y' as `this' argument of `void
X::Y::Bind(const
std::string&)' discards qualifiers

Now, I can't for the life of me see how I'm violating const rules
here. I'm not passing a const 'this' pointer. I'm passing a const
parameter, true, but it's not the this pointer.

std::set< T > should store T const's not T's, your looking at the wrong
"this", the error message is refering to the Y const * you were
using as a "this" paramiter to Y::Bind().

Note all the cruft I added to get this to compile, you might want to
think about changing, set< Y > _requests; to std::map< SomeKey, Y >.

HTH.

Rob.
 
T

tom_usenet

VC7 is wrong, If there is any doubt about this it compiles this
nonsence:

#include <iostream>
#include <ostream>
#include <set>

using namespace std;

int main()
{
set< int > si;
for ( int i = 0; i < 10; ++i ) si.insert( i );

*si.find( 2 ) = 4;

set< int >::iterator ptr, lim;

for ( ptr = si.begin(), lim = si.end(); ptr != lim; ++ptr )
{
cout << *ptr << endl;
}
}

This compiles with VC 7.1 ( Dinkumware Standard library 313 ).

And so it should - the code is well-formed according to the current
standard (set::value_type is non-const according to std::set's
synopsis, set::iterator::value_type is also non-const according to
container requirements). Obviously the code has undefined behaviour
(since a key is modified in such a way as to affect its ordering). See

http://std.dkuug.dk/jtc1/sc22/wg21/docs/lwg-defects.html#103

Tom

C++ FAQ: http://www.parashift.com/c++-faq-lite/
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
 
J

Jerry Coffin

[ ... ]
VC7 is wrong, If there is any doubt about this it compiles this
nonsence:

This is one of those places that's open to a lot of argument, at best.
Despite being more or less nonsensical, the standard requires a
conforming implementation to accept the code.

In any case, the library working group of the C++ committee has drafted
a proposal for fixing this part of the standard, and it'll almost
certainly end up in a TC or somesuch eventually. The proposal basically
says that for a set (among other things) the key will be immutable, and
both iterator and const_iterator will be const, so you won't be able to
modify the key as you've done.

Nonetheless, that's not part of the standard yet, so to follow the
standard as it currently stands, the compiler must accept the code.
There's a comment in the standard that more or less implies that if you
modify a key, the modification should not alter the ordering. Based on
that, I think a case could be made for saying that your code has
undefined behavior, meaning nothing the compiler does with it is really
wrong.
 
G

Gianni Mariani

Jerry said:
[ ... ]

VC7 is wrong, If there is any doubt about this it compiles this
nonsence:


This is one of those places that's open to a lot of argument, at best.
Despite being more or less nonsensical, the standard requires a
conforming implementation to accept the code.

In any case, the library working group of the C++ committee has drafted
a proposal for fixing this part of the standard, and it'll almost
certainly end up in a TC or somesuch eventually. The proposal basically
says that for a set (among other things) the key will be immutable, and
both iterator and const_iterator will be const, so you won't be able to
modify the key as you've done.

Nonetheless, that's not part of the standard yet, so to follow the
standard as it currently stands, the compiler must accept the code.
There's a comment in the standard that more or less implies that if you
modify a key, the modification should not alter the ordering. Based on
that, I think a case could be made for saying that your code has
undefined behavior, meaning nothing the compiler does with it is really
wrong.

Strictly speaking, you're 100% correct, however, in any case, gcc
behaviour is probably is *wrong* by the standard but its behaviour
(iterator is const) is exactly the right practical answer for me.

It just saved the OP hours of endless problems.

It would be better to have an implementation of a set that allows you to
"re-insert" an object rather than an erase and insert but the API for
STL containers seem to avoid allowing those kinds of more efficient (yep
more complex) uses of containers. So you end up placing those
complications in the application.
 
J

Jerry Coffin

[ ... ]
Strictly speaking, you're 100% correct, however, in any case, gcc
behaviour is probably is *wrong* by the standard but its behaviour
(iterator is const) is exactly the right practical answer for me.

Right -- that's exactly why I said it was open to argument instead of
saying something like "No, VC7 is right and gcc is wrong." The reality
is that pretty much everybody agrees that the standard is wrong the way
it stands, but it's still pretty hard for me to condemn somebody for
following the standard.

The same situation arises (in reverse) with exception specifications: as
specified, exception specifications are flawed to the point that
enforcing them is really a bad idea. VC++ will give you a warning in a
case like this:

void junk() throw() { throw 1; }

where it can show by static analysis that the code throws an exception
it says it won't. OTOH, it does NOT do the run-time enforcement that
causes the real problem. In this case, gcc is the one that follows the
standard (by calling unexpected() in this case) even though it's pretty
clearly the wrong thing to do.

In the end, there's no obvious, clear-cut way to handle things like this
-- especially when there's usually some room for argument that the way
the standard specifies things does have at least some minimal advantage.
In the case of modifying the key in a set, if you're SURE your change in
the key won't alter the order of elements in the set, then it's fastest
to modify it in place. Likewise, calls to unexpected() are likely to
help you find incorrect exceptions being thrown fairly quickly -- if
you're certain your testing will turn up all the problems before the
code is released, it _might_ work out reasonably well (from my comments
above, you can probably guess that I consider that unlikely at best).
 
J

John Calcote

tom_usenet said:
On 19 Dec 2003 14:27:10 -0800, (e-mail address removed) (John Calcote)
wrote:

It seems GCC is ahead of the game, already having implemented this
defect report:
http://std.dkuug.dk/jtc1/sc22/wg21/docs/lwg-defects.html#103

Tom (and everyone),

Thanks for the concise responses to my question. I now thoroughly
understand the problem domain. IMHO, Microsoft's approach to this
problem in the standard is more useful, if not necessarily more
correct.

As a programmer, I understand the fundamental concept of a set. It
would not occur to me to try to modify the key data in a set element
while it was in the set. While I could see doing this accidentally, I
don't necessarily think a compiler or library should prevent me from
doing it, because such precautions also prevent me from doing
reasonable things with set elements - such as changing non-key data.

As a side note, before I posted this message, I had changed Bind to
const, and then marked the data members touched by Bind as mutable. I
don't like this "solution" at all because frankly, it's nothing short
of casting away const on 'this', which lacks elegance, and generally
indicates design flaws. It's a hack required to accomplish something
entirely legitimate, and made necessary by an overbearing constraint.
Such constraints go against the grain of the original C and C++
language goals (remember the "shoot yourself in the foot" analogies
that used to float around years ago?)

In any case, I think I'll switch container types because my code has
to be portable to Windows, NetWare and 5 flavors of Unix. I can't take
a chance that a different interpretation of the standard in this area
will cause some future port to fail in a way that I can foresee and
prevent.

John
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top