Search code examples
c++stdvector

Is using begin() and end() of an empty std::vector well defined?


Presume that I am copying a std::vector into a std::vector with std::vector::insert (as below).

The question is: when the vector that I am copying from is empty, are begin() and end() well defined in all circumstances for purposes of the std::vector::insert call?

The reason I ask is this sentence:

If allocator_traits::construct is not supported with the appropriate arguments for the element constructions, or if an invalid position or range is specified, it causes undefined behavior.

https://cplusplus.com/reference/vector/vector/insert/

#include <vector>
#include <stdio.h>

void printbytes(char* bytes, size_t len) {
    if(len == 0) return;
    for(size_t i=0; i<len; i++)
    {
        printf("%x ", *(bytes+i) & 0xff);
    }
    printf("\n");
}

int main() {
    std::vector<char> target = {'h','e','l','l','o'};
    std::vector<char> source = {'w','o','r','l','d'};

    //defined
    target.insert(target.end(), source.begin(), source.end());
    printbytes(target.data(), target.size());

    //is this defined?
    source.clear();
    target.insert(target.end(), source.begin(), source.end());
    printbytes(target.data(), target.size());

    //how about this?
    std::vector<char> boo;
    boo.insert(boo.end(), source.begin(), source.end());
    printbytes(boo.data(), boo.size());
}

Solution

  • Firstly, begin() and end() always work. We know this because std::vector satisfies the Container requirement, and begin() is defined as follows:

    b.begin()
    

    Result: iterator; const_iterator for constant b.
    Returns: An iterator referring to the first element in the container.
    Complexity: Constant.

    - [container.reqmts]

    This function has no preconditions, so it must always work, even for empty, and even for moved-from vectors.

    Furthermore, all calls to insert you're making are well-defined because the requirements for a SequenceContainer in [sequence.reqmts] (which std::vector satisfies) require only the following for insert(p, i, j), where p, i, j are iterators:

    a.insert(p, i, j)
    

    Result: iterator.
    Preconditions: [Type constraints.] Neither i nor j are iterators into a.
    [...]

    Namely, it's not required by insert that [i, j) is a non-empty range.


    On a side note, cplusplus.com is massively out-of-date in a lot of cases. cppreference.com is more actively maintained, though it's sometimes wrong and incomplete. When in doubt, refer directly to the C++ standard.