Re: How do I remove GLIBCXX_ASSERTIONS?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 03/08/19 23:14 -0400, Sam Varshavchik wrote:
Andrew Lutomirski writes:

On a cursory search of the standard, I couldn't find where it says
what operator* on this type of iterator does at all, let alone whether
it's valid for one-past-the-end iterators, but I'm pretty sure that
your code is, indeed, wrong.

This finally piqued my curiosity enough to take some time to look into this. That expression applied the "*" operator to a random access iterator. So, going down the path:

# [random.access.iterators]
#
# A class or pointer type X satisfies the requirements of a random access# iterator if, in addition to satisfying the requirements for bidirectional# iterators, ...

In a similar fashion, a bidirectional iterator delegates some requirements to a forward iterator.

The forward iterator delegates some of its requirements to an input iterator.

At the end of the road, in [input.iterators]:

#  *a      reference,   convertible to T     Requires: a is dereferenceable.

Then, finally, in [iterator.requirements.general]:

# Values of an iterator i for which the expression *i is defined are called# dereferenceable.

This reads to me like a definition that circularly defines itself.
[input.iterators] says "*a requires that a is dereferencable". And [iterator.requirements.general] says that something is dereferencable if "*" for it is defined. That certainly clears that up…

Full disclosure: it's true that the next sentence is:

# The library never assumes that past-the-end values are dereferenceable.

But this only states that the library assumes that, it doesn't authoritatively state that.

Because some past-the-end iterators are dereferenceable. The
past-the-end iterator for a sequence [a,b) which is subsequence of
[a,c) will be dereferenceable is b!=c.

But the end() iterator for a container is both past-the-end and
non-dereferenceable, because it's not a subsequence of some larger
sequence. It really is past the end and there is no object at that
position.

But going back to the previous point, if we also want to see what going down "*i refers the unary * operator" route, as a means of avoiding the self-definition, we see that:

# [expr.unary.op]
#
# The unary * operator performs indirection: … the result is an lvalue

That's irrelevant in this case.  See [expr.pre] which says "Subclause
[expr.post] defines the effects of operators when applied to types for
which they have not been overloaded."

GCC's std::vector::iterator is not a pointer, it's a class with an
overloaded operator*, so its semantics are not required to be the same
as the built-in * on pointers.

Nothing here requires the pointer to be valid, just that "*" gives you an lvaule. Nothing more, nothing else. That's it. Whether the pointer

Wrong. An lvalue can only refer to a valid object.

is valid, this gets punted only when the lvalue-to-rvalue conversion take place:

Nope ...

# [conv.lval]
#
# ... if the object to which the glvalue refers contains an invalid pointer
# value the behavior is implementation-defined.

No, that is not saying you can have an lvalue formed by dereferencing
an invalid pointer value. It's saying that applying the
lvalue-to-rvalue conversion to an invalid pointer value is
implementation defined. i.e.

 void lvalue_to_rvalue_conv(int*);

 int* ptr = new int(0);
 delete ptr;
 lvalue_to_rvalue_conv(ptr); // implementation defined

That wording was added by https://wg21.link/cwg616 and is irrelevant
here. Dereferencing invalid or null pointer values is not allowed in
C++.

But if you immediately apply the & operator, this conversion never takes place.

True in C, not in C++.

The C standard is even more explicit, and even blesses this construct, verbatim:

# The unary & operator yields the address of its operand.
# [ … ]
#
# if the operand is the result of a [] operator, neither the & operator # nor the unary * that is implied by the [] is evaluated and the result is as # if the & operator were removed and the [] operator were changed to a + # operator

I could not find some similar verbiage in the C++ standard, but based

You can't find it because there is no such rule in C++. There is an
open issue asking whether it should be allowed, but it's been open
(and inactive) for many years. See https://wg21.link/cwg232

on all of the above I have to conclude that this is …too late today, and I should really get some sleep…

Anyway, even if that issue was resolved, it would be irrelevant when
considering an overloaded operator*.

Applying & to the result of an overloaded operator* still has to
evaluate the overloaded operator* and then evaluate & (which might
also use an overloaded operator&). So any rule that said that &*p is a
no-op when p is a pointer would not apply when p is a class type, like
GCC's std::vector::iterator.


But not after rereading the specification for a vector itself, where I found something I glossed over the first time:

# [vector]
#
# A vector satisfies all of the requirements of a container and … of a# contiguous container.

The reference from "contiguous container" goes to:

# [container.requirements.general]
#
# A contiguous container is a container that supports random access iterators
# and whose member types iterator and const_iterator are contiguous iterators.

The reference from "contiguous iterators" goes to:

# [iterator.requirements.general]
#
# Iterators that further satisfy the requirement that, for integral values
# n and dereferenceable iterator values a and (a + n), *(a + n) is
# equivalent to *(addressof(*a) + n), are called contiguous iterators.

If n is foo.size() then (a + n) is not a dereferenceable iterator, so
the *(addressof(*a) + n) equivalence doesn't hold. Because *a is
undefined.

There might be more to the "contiguous" semantics that reflects on whether or not "&foo[foo.size()]" is defined behavior, or not, but

There isn't.

here addressof(*a) already puts us squarely in pointer equivalence territory, and seems to again go into the unary "*" operator direction

Nope, it does whatever std::vector::iterator::operator* does. Which is
not necessarily the same as the built-in unary * for pointers.

here, despite the conflicting wording.

It's undefined. GCC's assertion is correct. Fix the code.

…now it's definitely too late in the day.
_______________________________________________
devel mailing list -- devel@xxxxxxxxxxxxxxxxxxxxxxx
To unsubscribe send an email to devel-leave@xxxxxxxxxxxxxxxxxxxxxxx
Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: https://lists.fedoraproject.org/archives/list/devel@xxxxxxxxxxxxxxxxxxxxxxx




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Fedora Announce]     [Fedora Users]     [Fedora Kernel]     [Fedora Testing]     [Fedora Formulas]     [Fedora PHP Devel]     [Kernel Development]     [Fedora Legacy]     [Fedora Maintainers]     [Fedora Desktop]     [PAM]     [Red Hat Development]     [Gimp]     [Yosemite News]

  Powered by Linux