2

Consider following code:

vector<uint8_t> v(1);
v.reserve(2);
uint8_t *data = &v.front();
data[1] = 0;

Is there undefined behavior (C++98, C++03, C++11)?
And if yes, what is best way to get RAII buffer (not using C++11)?

Andy
  • 1,036
  • 12
  • 14
  • To all answering please note I used reserve() not resize() here. – Andy Jul 20 '15 at 12:43
  • 3
    The best way to get a buffer of size 2 is to use `resize` instead of `reserve` (or make it size `2` in the first place) – M.M Jul 20 '15 at 12:44
  • 1
    For real life applications, I would first try `vector::resize`. Usually the extra initialization cost does not matter (never has for me so far). If it should be too slow in your case, you can still go with `boost::unique_ptr`. – Baum mit Augen Jul 20 '15 at 12:52
  • And one can probably proof (if my guts are right) that this is fine at least for POD, but do you really want to go to those edge-cases in real life code that some day might be read and changed by non language-lawyers? – Baum mit Augen Jul 20 '15 at 12:57

2 Answers2

5

It is undefined behaviour to call v.front() whenever v.empty() is true. It is undefined behaviour to call v[n] unless n < v.size(). Moreover, there are no objects at the reserved memory, so you cannot treat the memory as if it was an object. A vector only guranatees

that [data(), data() + size()) is a valid range

and there are no guarantees that there is any larger valid range. (Note that data() == &front(), so this is applies to your code.)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 4
    Please not there is one element in vector therefore it is not empty. – Andy Jul 20 '15 at 12:28
  • 3
    Please note I do not call [] on vector. – Andy Jul 20 '15 at 12:32
  • But the memory is there(?), if so, it is contiguous and `uint8_t` does not need constructor calls (is even POD). The deeper question would be whether `reserve` can be ignored or delayed under the as-if-rule. – Baum mit Augen Jul 20 '15 at 12:59
  • Yes, that is the question:) Seems obvious that this is not the best tool for the job. What else can we use to obtain dynamically allocated buffer with RAII semantics? – Andy Jul 20 '15 at 13:05
  • 1
    @Andy Have you measured that `std::vector::resize` is too slow for your application? Setting some `char`s to zero is *really* fast on modern machines. – Baum mit Augen Jul 20 '15 at 13:25
  • @BaummitAugen Yes, and in [some cases](http://stackoverflow.com/a/26278335/3422652) I've found the compiler is smart enough to optimize out the zeroing out. – Chris Drew Jul 20 '15 at 13:47
  • Maybe it is slower, maybe not. Maybe compiler is smart, maybe not. But the tool is not designed for this job. – Andy Jul 20 '15 at 13:51
  • 1
    @Andy I really must disagree with that view. `std::vector` is *the* standard container for runtime sized collections (and big or big-ish compile time sized collections too). It is the right tool until proven otherwise. – Baum mit Augen Jul 20 '15 at 13:55
  • My effort here is to underline "no initialization". That's why I have used reserve(). Of course I can use resize() and be on the safe side, but seems that vector is build with initialization safety in focus. That's why this tool (vector) is not what I need here. – Andy Jul 20 '15 at 14:00
  • 2
    @Andy: There aren't any containers in the standard library at the moment for uninitialized storage. There are some helper algorithms (e.g. `std::uninitizlied_fill`), but no containers. If you need one, write your own. – Kerrek SB Jul 20 '15 at 14:03
-1

I have checked C++98 standard.

Here is note for reserve():
"It is guaranteed that no reallocation takes place during insertions that happen after a call to reserve() until the time when an insertion would make the size of the vector greater than the size specified in the most recent call to reserve()."

And from note for "vector modifiers":
"If no reallocation happens, all the iterators and references before the insertion point remain valid."

Therefore IMHO code presented in question is perfectly valid even for C++98.

Andy
  • 1,036
  • 12
  • 14
  • It is also undefined behaviour to access uninitialized memory. So "perfectly valid" is quite an overstatement. – rubenvb Jun 09 '16 at 13:07
  • Is it undefined behaviour to initialize uninitialized fundamental type object? Not sure where standard says so. – Andy Jun 09 '16 at 20:28
  • Please consider following code: "int a;" Is it undefined behaviour to access 'a'? – Andy Jun 09 '16 at 20:39
  • Let me rephrase, see what @Kerrek says. – rubenvb Jun 10 '16 at 05:32
  • So, is it valid or not? If not why? Please give citation from standard (as I did). – Andy Jun 10 '16 at 05:58
  • Please read all comments under @Kerrek answer. The key point/question here - is there undefined behavior or not? – Andy Jun 10 '16 at 06:13
  • Kerrek quoted from the Standard. Note there is really only one element in the OP's vector, all the rest is undefined behaviour as far as C++ is concerned. – rubenvb Jun 10 '16 at 06:17
  • There is no citation in Kerrek's answer. Please read about vector's 'capacity()' and reallocation then reason about it. – Andy Jun 10 '16 at 06:32
  • There is nothing to reason about. There is no guarantee. There are a lot of closely related things to that you do that are explicitly marked as undefined behaviour. The fact that you mask this by access through a pointer doesn't make it any better. Any operator[] access outside of the size is undefined behaviour, 23.3.6.1/1. – rubenvb Jun 10 '16 at 06:47