Assignment operator self-assignment check

F

Frederick Gotham

Noah Roberts posted:
int array1[5], array2[5], *p1 = array1+3, *p2 = array2+2;

p2 > p1; /* UB! */

I don't really even know what you are talking about here as your
clipping has completely removed context but I can see right off that
there is no undefined behavior in the above code. I can't imagine why
you think there is.


The above code snippet invokes undefined behaviour because the two pointers
which it compares do _not_ point to the same array.
 
N

Noah Roberts

Frederick said:
Noah Roberts posted:
int array1[5], array2[5], *p1 = array1+3, *p2 = array2+2;

p2 > p1; /* UB! */

I don't really even know what you are talking about here as your
clipping has completely removed context but I can see right off that
there is no undefined behavior in the above code. I can't imagine why
you think there is.


The above code snippet invokes undefined behaviour because the two pointers
which it compares do _not_ point to the same array.

Why would that invoke undefined behavior?? We can't tell offhand what
the result of the operation you've labeled is going to be (well
actually it does nothing but lets assume you assigned the result to a
boolean) and the information is rather useless, but there is no
undefined behavior that I see.
 
F

Frederick Gotham

Noah Roberts posted:
Why would that invoke undefined behavior??

Forgive me, the behaviour is "unspecified" rather than "undefined".

Page 88 of the current Standard:

| If two pointers p and q of the same type point to different objects
| that are not members of the same object or elements of the same
| array or to different functions, or if only one of them is null,
| the results of p<q, p>q, p<=q, and p>=q are unspecified.

It looks like the following program may print either "true" or "false"
regardless of whether the numerical address of "a" is greater than the
numerical address of "b":

#include <iostream>

#define TF(expr) ( (expr) ? "true" : "false" )

int main()
{
int a,b;

std::cout << TF(&b > &a);
std::cout << TF( (long unsigned)&b > (long unsigned)&a );
}

It's possible that this can print two different answers, but the behaviour
is nonetheless well-defined.
 
F

Frederick Gotham

Frederick Gotham posted:
std::cout << TF( (long unsigned)&b > (long unsigned)&a );


Let's assume for the purposes of this discussion that an unsigned long
provides enough bits to store the address of an int.
 
S

S S

Kai-Uwe Bux said:
I think a reference counted non-intrusive smart pointer could benefit from
the first version. Consider the following schematic implementation:

template < typename T >
class refcount_ptr;

template < typename T >
void swap ( refcount_ptr< T > &, refcount_ptr< T > & );

template < typename T >
class refcount_ptr {

friend void swap<> ( refcount_ptr<T> &, refcount_ptr<T> & );

struct T_count {

T* t_ptr;
unsigned long ref_count;

T_count ( T * ptr )
: t_ptr( ptr )
, ref_count ( 1 )
{}

~T_count ( void ) {
delete ( t_ptr );
}

};

T_count * c_ptr;

public:

refcount_ptr ( T * ptr = 0 )
: c_ptr( new T_count ( ptr ) )
{}

refcount_ptr ( refcount_ptr const & other )
: c_ptr ( other.c_ptr )
{
++ c_ptr->ref_count;
}

~refcount_ptr ( void ) {
-- c_ptr->ref_count;
if ( c_ptr->ref_count == 0 ) {
delete( c_ptr );
}
}

refcount_ptr & operator= ( refcount_ptr const & other ) {
if ( c_ptr != other.c_ptr ) {
this->~refcount_ptr();
new ( this ) refcount_ptr( other );
}
return( *this );
}

T const * operator-> ( void ) const {
return( c_ptr->t_ptr );
}

T * operator-> ( void ) {
return( c_ptr->t_ptr );
}

// more stuff

};

In this case, the additional cost of the comparison could be zero: the
refcount_ptr objects fit within registers. It might very well be that
comparing the values of the c_ptr members is more efficient than comparing
their addresses. On the other hand, the value comparison will safe on
hidden self-assignments, which actually could be quite frequent depending
on the application.


Best

Kai-Ue Bux

You mean we have to inherit from this class? If yes, we can hung up
with multiple inheritance!!! and this might not be desirable solution
at all.

-SS
 
K

Kai-Uwe Bux

S said:
You mean we have to inherit from this class?

No. What made you think that?

Note refcount_ptr<T> is just a watered down version of tr1::shared_ptr<T>
for the sake of exposision: It does not feature a custom deleter and I left
out the methods like operator<, operator==, ...; and I also did not support
for making refcount_ptr<D> and refcount_ptr<T> assignment compatible it D*
and T* are. However, the intended use is just like tr1::shared_ptr<T>, in
If yes, we can hung up with multiple inheritance!!! and this might not be
desirable solution at all.

What would be wrong with multiple inheritance?


Best

Kai-Uwe Bux
 
N

Noah Roberts

Frederick said:
Noah Roberts posted:


Forgive me, the behaviour is "unspecified" rather than "undefined".

These are very different.
Page 88 of the current Standard:

When quoting the standard it is better to use section numbers instead
of page numbers. It is page 100 of the one I'm looking at right now.
The section you are quoting is 5.9
| If two pointers p and q of the same type point to different objects
| that are not members of the same object or elements of the same
| array or to different functions, or if only one of them is null,
| the results of p<q, p>q, p<=q, and p>=q are unspecified.

It looks like the following program may print either "true" or "false"
regardless of whether the numerical address of "a" is greater than the
numerical address of "b":

#include <iostream>

#define TF(expr) ( (expr) ? "true" : "false" )

int main()
{
int a,b;

std::cout << TF(&b > &a);
std::cout << TF( (long unsigned)&b > (long unsigned)&a );
}

It's possible that this can print two different answers, but the behaviour
is nonetheless well-defined.

I don't think you are interpreting that paragraph correctly. Placing
your quote into context with the section it is found in you have things
such as the paragraph before the one you quote:

"If two pointers p and q of the same type point to the same object or
function, or both point one past the end of the same array, or are both
null, then p<=q and p>=q both yield true and p<q and p>q both yield
false."

And the one following:

"If two pointers point to non-static data members of the same object,
or to subobjects or array elements of such members, recursively, the
pointer to the later declared member compares greater provided the two
members arenot separated by an access-specifier label (11.1) and
provided their class is not a union."

Both of the two exceptions also have paragraphs dictating exactly how
the comparison will return. Unspecified in the former case, equal in
the later.

With this context in mind "specified" means exactly which value will be
returned by the operation and gives us some pretty clear rules about
the layout of classes and arrays. Because the locations of a and b are
not defined, in our case, the comparison of their address cannot be
specified as has been clearly done with other cases. "Unspecified" in
this case simply means that there is no way of knowing before hand what
the return of that operation will be as there are no rules governing
the relative locations of a and b; it doesn't mean it won't be a valid
comparison which is what your interpretation is.

Your interpretation of "unspecified" leans closer to "undefined"
meaning there is nothing governing what the result will be...that isn't
the case here. We can still assume the camparison is a valid one and
will result in true/false depending on the relative location in memory
of these two objects. "Unspecified" does not indicate otherwise, it
just means the implementation is free to put these objects anywhere it
wants in memory, which is different to such cases when the result is
specified.
 
N

Noah Roberts

Frederick said:
Noah Roberts posted:


Forgive me, the behaviour is "unspecified" rather than "undefined".

Thinking about this more I have a hard time understanding why you are
trying to convince me that the comparison you perform in your assert
could create strange and meaningless results. If you believe such is
the case then you should not be using the assert in question at all.
You're just convincing me that in this case I am correct...while you
seem hell bent on proving me wrong :p
 
J

Jerry Coffin

[ ... ]
The behaviour is undefined if you compare two pointers which don't point
into the same array. The following invokes UB:

int array1[5], array2[5], *p1 = array1+3, *p2 = array2+2;

p2 > p1; /* UB! */

As you've already noted, it's unspecified rather than undefined.

More importantly, see 20.3.3/8, and simply use:

bool whatever = std::less(p1,p2);

From a viewpoint of determining whether something involves self-
assignment, we're mostly concerned with the fact that the two pointers
either consistently compare equal or non-equal -- and std::less gives us
that.
 
N

Noah Roberts

Jerry said:
From a viewpoint of determining whether something involves self-
assignment, we're mostly concerned with the fact that the two pointers
either consistently compare equal or non-equal -- and std::less gives us
that.

So does the equality operator. If two pointers represent the same
address they compare equal. This of course will be governed by any
necissary conversions to come up with the "composite pointer type."

See 5.10.1
 
J

Jerry Coffin

[ ... ]
With this context in mind "specified" means exactly which value will be
returned by the operation and gives us some pretty clear rules about
the layout of classes and arrays. Because the locations of a and b are
not defined, in our case, the comparison of their address cannot be
specified as has been clearly done with other cases. "Unspecified" in
this case simply means that there is no way of knowing before hand what
the return of that operation will be as there are no rules governing
the relative locations of a and b; it doesn't mean it won't be a valid
comparison which is what your interpretation is.

While the comparison is _valid_ (at least to the extent that it's
required not to crash the machine or something like that, like UB would
allow) it's not required to produce meaningful results. For example, it
could be unstable -- comparing the same pointers two different times
might produce different results.

As noted elsethread, however, std::less gives a total ordering even when
the native comparison operators don't.
 
J

Jerry Coffin

[ ... ]
So does the equality operator. If two pointers represent the same
address they compare equal. This of course will be governed by any
necissary conversions to come up with the "composite pointer type."

See 5.10.1

Quite true -- actually, I haven't followed the thread closely enough to
be sure how < entered it at all. OTOH, I can see situations where you're
writing a template that might deal with a pointer or an object, and in
that case I can see situations where you might want to determine
equality (or lack of it) based only on less-than, since a fair number of
ordered types only support less-than. Offhand, I'm not sure how that'd
apply specifically to the case of self-assignment though...
 
N

Noah Roberts

Jerry said:
[ ... ]
So does the equality operator. If two pointers represent the same
address they compare equal. This of course will be governed by any
necissary conversions to come up with the "composite pointer type."

See 5.10.1

Quite true -- actually, I haven't followed the thread closely enough to
be sure how < entered it at all.

Yes, you would have to go back a few messages to figure that out. It
was a side mention that became the major issue.
OTOH, I can see situations where you're
writing a template that might deal with a pointer or an object, and in
that case I can see situations where you might want to determine
equality (or lack of it) based only on less-than, since a fair number of
ordered types only support less-than.

Well, in cases when comparison of two pointers would actually be useful
it is fully specified. Even with std::less you can't tell if the two
pointers are actually related in some way since if they aren't then
their relative location is meaningless. I personally can't think of
any situation in which std::less would return a more meaningful value
than < even if you ran into some obscure implementation that didn't use
the address value for comparison in both cases. I also don't think
you'll ever find such an implementation as the other requirements
surrounding comparison are so strict as to not allow for any other
method I can think of.

At any rate, unspecified and undefined are totally different and I
don't dispute the unspecified nature of comparing unrelated
pointers...in fact that was really my point to begin with.
Offhand, I'm not sure how that'd
apply specifically to the case of self-assignment though...

It doesn't. This conversation lost track a long time ago :p
 
N

Noah Roberts

Jerry said:
[ ... ]
So does the equality operator. If two pointers represent the same
address they compare equal. This of course will be governed by any
necissary conversions to come up with the "composite pointer type."

See 5.10.1

Quite true -- actually, I haven't followed the thread closely enough to
be sure how < entered it at all.

Yes, you would have to go back a few messages to figure that out. It
was a side mention that became the major issue.
OTOH, I can see situations where you're
writing a template that might deal with a pointer or an object, and in
that case I can see situations where you might want to determine
equality (or lack of it) based only on less-than, since a fair number of
ordered types only support less-than.

Well, in cases when comparison of two pointers would actually be useful
it is fully specified. Even with std::less you can't tell if the two
pointers are actually related in some way since if they aren't then
their relative location is meaningless. I personally can't think of
any situation in which std::less would return a more meaningful value
than < even if you ran into some obscure implementation that didn't use
the address value for comparison in both cases. I also don't think
you'll ever find such an implementation as the other requirements
surrounding comparison are so strict as to not allow for any other
method I can think of.

At any rate, unspecified and undefined are totally different and I
don't dispute the unspecified nature of comparing unrelated
pointers...in fact that was really my point to begin with.
Offhand, I'm not sure how that'd
apply specifically to the case of self-assignment though...

It doesn't. This conversation lost track a long time ago :p
 
S

S S

Kai-Uwe Bux said:
No. What made you think that?

Note refcount_ptr<T> is just a watered down version of tr1::shared_ptr<T>
for the sake of exposision: It does not feature a custom deleter and I left
out the methods like operator<, operator==, ...; and I also did not support
for making refcount_ptr<D> and refcount_ptr<T> assignment compatible it D*
and T* are. However, the intended use is just like tr1::shared_ptr<T>, in


What would be wrong with multiple inheritance?

In industry practice, it is always recommended not to use multiple
inheritance at any cost. It will lead to ambiguous code (virtual
functions).
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top