Nice way to append a vector to itself
Wow. So many answers that are close, none with all the right pieces. You need both resize
(or reserve
) and copy_n
, along with remembering the original size.
auto old_count = xx.size();
xx.resize(2 * old_count);
std::copy_n(xx.begin(), old_count, xx.begin() + old_count);
or
auto old_count = xx.size();
xx.reserve(2 * old_count);
std::copy_n(xx.begin(), old_count, std::back_inserter(xx));
When using reserve
, copy_n
is required because the end()
iterator points one element past the end... which means it also is not "before the insertion point" of the first insertion, and becomes invalid.
23.3.6.5 [vector.modifiers]
promises that for insert
and push_back
:
Remarks: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid. If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T or by any InputIterator operation there are no effects. If an exception is thrown by the move constructor of a non-CopyInsertable T, the effects are unspecified.
Append vector to itself?
Range-for is syntactic sugar for an iterator-based for-loop, and std::vector::push_back()
can invalidate iterators like the ones being used internally by the range-for:
If the new
size()
is greater thancapacity()
then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.
You can use std::copy()
after a resize()
:
vector<int> a;
a.push_back(2);
a.push_back(3);
auto size = a.size();
a.resize(size * 2);
std::copy(a.begin(), a.begin() + size, a.begin() + size);
Wrong results when appending vector to itself using copy and back_inserter
std::back_inserter
creates an inserting iterator that inserts elements into a container. Each time this iterator is dereferenced, it calls push_back
on the container to append a new element to the container.
For a std::vector
container, a call to push_back
where v.size() == v.capacity()
will result in a reallocation: a new array is created to store the contents of the vector, its current contents are copied into the new array, and the old array is destroyed. Any iterators into the vector at this time are invalidated, meaning they can no longer be used.
In your program, this includes the input range defined by begin(vec)
and end(vec)
from which the copy
algorithm is copying. The algorithm continues to use these iterators, even though they are invalidated, thus your program exhibits undefined behavior.
Even if your container had sufficient capacity, its behavior would still be undefined: the specification states that, upon insertion, "if no reallocation happens, all the iterators and references before the insertion point remain valid" (C++11 §23.3.6.5/1).
The call to push_back
is equivalent to insertion at the end, so the end iterator (std::end(vec)
) that you passed into std::copy
is invalidated after a single call to push_back
. If the input range is nonempty, the program therefore exhibits undefined behavior.
Note that the behavior of your program would be well-defined if you used a std::deque<int>
or a std::list<int>
, because neither of those containers invalidates iterators when elements are appended.
Appending std::vector to itself, undefined behavior?
According to the C++03 ISO spec (§23.1.1, Table 67) (and as @AndyProwl has mentioned, in §23.2.3, table 11 of the C++11 ISO spec), as part of sequence requirements, the operation a.insert(p, i, j)
in a sequence container has this precondition:
i
,j
are not iterators intoa
.
In other words, sequence containers are allowed to safely assume that if you do a range insertion operation, that range will not be defined from iterators over that original container.
As a result, if you try to insert a container's elements into itself, you are calling a standard library function and breaking a precondition. This results in undefined behavior, meaning that it might work on some platforms if the library implementers are nice people, but it could terribly and catastrophically fail with no justification.
Hope this helps!
Best way to append vector to vector
In my opinion, your first solution is the best way to go.
vector<>::insert
is designed to add element so it's the most adequate solution.
You could call reserve
on the destination vector to reserve some space, but unless you add a lot of vector together, it's likely that it wont provide much benefits: vector<>::insert
know how many elements will be added, you will avoid only one reserve
call.
Note: If those were vector
of more complex type (ie a custom class, or even std::string
), then using std::move
could provide you with a nice performance boost, because it would avoid the copy-constructor. For a vector of int
however, it won't give you any benefits.
Note 2: It's worth mentioning that using std::move
will cause your source vector
's content to be unusable.
C++ append one vector to another
If you're in the position to change scanDir
, make it a (template) function accepting an output iterator:
template <class OutIt>
void scanDir(const std::string& dirname, OutIt it) {
// ...
// Scan subdir
scanDir(subdir, it);
// ...
}
You'll have the additional benefit to be able to fill all sort of data structures like
std::vector<string> vector;
scanDir(dir1, std::back_inserter(vector));
std::set<string> fileset
scanDir(dir1, std::inserter(fileset, fileset.begin()));
etc.
EDIT (see comment ...)
For using this function for class member initialization, you could either call it in the constructor as in
class MyClass {
private:
std::vector<string> m_fileList;
public:
MyClass(const std::string& dirname) {
scanDir(dirname, std::back_inserter(m_fileList);
}
}
or using a wrapper function
std::vector<string> scanDir(const std::string& dirname) {
std::vector<string> result;
scanDir(dirname, std::back_inserter(result);
return result;
}
class MyClass {
// Same as above..
MyClass(const std::string& dirname) : m_fileList(scanDir(dirname)) { }
}
I would prefer the first version for performance (and other) reasons ...
Under-the-hood STL: Concatenating an std::vector to itself without new vector
You will copy items indefinitely. Note how many to copy up front:
size_t n = aVec.size();
for (int k = 0; k != n; ++k)
aVec.push_back(aVec[k]);
While many C++ algorithms are better expressed using begin()
and end()
iterators, in this case your access via index is superior, because modifying the container might invalidate the iterators, but the element access will remain valid. You can, however, use reserve
to avoid that invalidation:
aVec.reserve(2*aVec.size());
std::copy(aVec.begin(), aVec.end(), std::back_inserter(aVec));
Can I insert a vector to itself with std::vector::insert?
Regardless of whether perform reserve
in advance or not, the behavior is undefined. For std::vector::insert
:
inserts elements from range [first, last) before pos.
The behavior is undefined if first and last are iterators into
*this
.
Related Topics
How to Create My Own Comparator For a Map
How Many and Which Are the Uses of "Const" in C++
Trailing Return Type Using Decltype With a Variadic Template Function
Optional Parameters With C++ Macros
How to Programmatically Get the Version of a Dll or Exe File
Why Is My Integer Math With Std::Pow Giving the Wrong Answer
Returning Arrays from a Function in C++
How to Get the Argument Types of a Function Pointer in a Variadic Template Class
Why Is Default Constructor Called in Virtual Inheritance
C/C++ Include Header File Order
Initializing Fields in Constructor - Initializer List VS Constructor Body
How to Select a Range of Values in a Switch Statement
Static Variables in Member Functions
What Is Constructor Inheritance
Overload Resolution Failure When Streaming Object Via Implicit Conversion to String