smart vector - why?

Started by
13 comments, last by MaulingMonkey 15 years, 4 months ago
Quote:Original post by SiCrane
Quote:Original post by Antheus
Although there's lots of talk about undefined behavior, it should be noted that in this particular case (considering actual implementation of std::vector, it isn't).


No, this is undefined. The operational semantics of v[n] on a vector v is *(v.begin() + n). If n is greater than or equal to the size of v, then you're dereferencing an iterator at end() or past end(). This is undefined behavior. See sections 23 and 24 in the C++ Standard, in particular section 23.1.1 paragraph 12.


As it turns out, it depends on the type stored in vector. In case of MSVC, when a value is removed from a vector, _Destroy will be called on it.

Unfortunately, _Destroy doesn't do anything in case of basic types. End result is quite simply that the memory location does remain valid, so does the value.

I agree that it's undefined per standard. But like I said, the end result is unfortunately not undefined behavior in case of types for which (in this case) _Destroy doesn't do anything.

In some cases, algorithms were designed which took into consideration such implicit life-span of values in memory. While fragile in nature, the algorithm itself could be made to work in a completely deterministic manner.

I consider this a much more serious problem than typical invalid memory references, since the point at which such value goes from valid reference to invalid memory location is deterministic, and may never occur. It may even be safely, and to a degree portably exploited.

I would also consider this to be very representative of what makes C++ such problematic language. This is one of those corner cases which just calls for errors. Again, I'm pragmatic about what C++ standard means and am more concerned with what one encounters in practice.

As a side note - memory pools and pre-allocated storage deliberately rely on exactly the same behavior to avoid crashes. When dealing with lock-less structures, this type of references are almost required to avoid access to unallocated memory, since you never know if anyone is still touching the memory that you wish to release (a condition which cannot be practically checked without waiting, defeating the purpose of lock-less algorithms).
Advertisement
Quote:Original post by Antheus
As it turns out, it depends on the type stored in vector.

No, it doesn't. Even on MSVC if you compile things one way it may "work" like you describe, or it could cause a runtime error if you compile things another way. This is the essence of undefined behavior: it can do anything. This is both a matter of standardization: the standard says that it's undefined, and a matter of practice: indexing outside the bounds of a vector, even for a POD type, can have widely different behavior depending on your compiler, operating system or standard library implementation.
Quote:Original post by SiCrane
Quote:Original post by Antheus
As it turns out, it depends on the type stored in vector.

No, it doesn't. Even on MSVC if you compile things one way it may "work" like you describe, or it could cause a runtime error if you compile things another way. This is the essence of undefined behavior: it can do anything. This is both a matter of standardization: the standard says that it's undefined, and a matter of practice: indexing outside the bounds of a vector, even for a POD type, can have widely different behavior depending on your compiler, operating system or standard library implementation.

Phases of the moon, when the last election was, the results of the last poll, the number of times your computer has crashed, how much wood a woodchuck could chuck if a woodchuck could chuck wood.

As SiCrane has said (and I hope I've demonstrated above) the behavior of undefined behavior is undefined. An implementation is allowed to perform implementation defined behavior upon undefined behavior, however in the general case that remains undefined behavior.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Quote:Original post by Washu
the behavior of undefined behavior is undefined

Does behavior really have behavior? I'd say the behavior of something that has undefined behavior is undefined.
Quote:Original post by Antheus
Quote:Original post by SiCrane
Quote:Original post by Antheus
Although there's lots of talk about undefined behavior, it should be noted that in this particular case (considering actual implementation of std::vector, it isn't).


No, this is undefined. The operational semantics of v[n] on a vector v is *(v.begin() + n). If n is greater than or equal to the size of v, then you're dereferencing an iterator at end() or past end(). This is undefined behavior. See sections 23 and 24 in the C++ Standard, in particular section 23.1.1 paragraph 12.


As it turns out, it depends on the type stored in vector. In case of MSVC, when a value is removed from a vector, _Destroy will be called on it.

Unfortunately, _Destroy doesn't do anything in case of basic types. End result is quite simply that the memory location does remain valid, so does the value.


Irrelevant -- both due to the fact that it's an implementation detail, and the fact that said implementation detail also has no bearing on the implementation of v[n]. In the case, again, of MSVC (2008), when you try to access such "removed" elements, taking the originally posted example in debug mode...

We get a debug assertion failure. This is perfectly legal behavior under "undefined behavior", and the exact kind of thing that causes us to be completely against labeling implementation specific deterministic behavior as "defined behavior".

Quote:I would also consider this to be very representative of what makes C++ such problematic language. This is one of those corner cases which just calls for errors. Again, I'm pragmatic about what C++ standard means and am more concerned with what one encounters in practice.

That's great and all, but there's a surprising number of times that seemingly... harmless instances of UB can and will turn around and cause problems. Since I am not omniscient, capable of seeing every possible problem caused by even those cases of UB, prudence dictates I try to avoid all cases of UB, given how easy it is to do so once you've identified it.

This topic is closed to new replies.

Advertisement