std::copy 向量无法正常工作
std::copy for vector doesn't work properly
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> a{ 1, 2, 3 };
copy(a.begin(), a.end(), back_inserter(a));
for (const int& x : a)
cout << x << ' ';
}
输出:
1 2 3 1 -572662307 -572662307
预期输出:
1 2 3 1 2 3
我不知道为什么会这样。这种行为的原因是什么?
问题是随着向量的增长,您提供的迭代器可能会失效。您可以使用 reserve
来解决这个问题。通常,如果您提前知道大小,那么使用 reserve
是个好主意,这样进行的分配就更少了:
#include <algorithm>
#include <vector>
int main() {
std::vector<int> a{1, 2, 3};
a.reserve(a.size() * 2);
a.insert(a.end(), a.begin(), a.end());
}
请注意 insert
通常比 back_inserter
更好更简单。
来自 cppreference 的 copy
的可能实现:
template<class InputIt, class OutputIt>
OutputIt copy(InputIt first, InputIt last,
OutputIt d_first)
{
while (first != last) {
*d_first++ = *first++;
}
return d_first;
}
使用 back_inserter
*first++
将在矢量上调用 push_back
。当向量需要重新分配时,调用 push_back
可能会使所有迭代器失效。因此,您的代码具有未定义的行为。
请注意 back_inserter
有点异国情调。它违反了迭代器和容器通常的严格分离,因为迭代器必须存储对容器的引用。仅这一点并不能解释您看到的效果,但它表明当迭代器实际修改容器时需要小心。
通常您的程序具有未定义的行为,因为迭代器在向向量添加新元素期间可能会变得无效。
你必须在向量中预留足够的内存。例如
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::vector<int> v = { 1, 2, 3 };
v.reserve( 2 * v.size() );
std::copy( std::begin( v ), std::end( v ), std::back_inserter( v ) );
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
return 0;
}
如果您的编译器支持 C++ 17 标准(在 C++ 14 标准中,要求指定复制范围的迭代器不是向量本身的迭代器),那么您可以使用插入方法,例如
#include <iostream>
#include <vector>
#include <iterator>
int main()
{
std::vector<int> v = { 1, 2, 3 };
v.insert( std::end( v ), std::begin( v ), std::end( v ) );
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
return 0;
}
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> a{ 1, 2, 3 };
copy(a.begin(), a.end(), back_inserter(a));
for (const int& x : a)
cout << x << ' ';
}
输出:
1 2 3 1 -572662307 -572662307
预期输出:
1 2 3 1 2 3
我不知道为什么会这样。这种行为的原因是什么?
问题是随着向量的增长,您提供的迭代器可能会失效。您可以使用 reserve
来解决这个问题。通常,如果您提前知道大小,那么使用 reserve
是个好主意,这样进行的分配就更少了:
#include <algorithm>
#include <vector>
int main() {
std::vector<int> a{1, 2, 3};
a.reserve(a.size() * 2);
a.insert(a.end(), a.begin(), a.end());
}
请注意 insert
通常比 back_inserter
更好更简单。
来自 cppreference 的 copy
的可能实现:
template<class InputIt, class OutputIt> OutputIt copy(InputIt first, InputIt last, OutputIt d_first) { while (first != last) { *d_first++ = *first++; } return d_first; }
使用 back_inserter
*first++
将在矢量上调用 push_back
。当向量需要重新分配时,调用 push_back
可能会使所有迭代器失效。因此,您的代码具有未定义的行为。
请注意 back_inserter
有点异国情调。它违反了迭代器和容器通常的严格分离,因为迭代器必须存储对容器的引用。仅这一点并不能解释您看到的效果,但它表明当迭代器实际修改容器时需要小心。
通常您的程序具有未定义的行为,因为迭代器在向向量添加新元素期间可能会变得无效。
你必须在向量中预留足够的内存。例如
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::vector<int> v = { 1, 2, 3 };
v.reserve( 2 * v.size() );
std::copy( std::begin( v ), std::end( v ), std::back_inserter( v ) );
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
return 0;
}
如果您的编译器支持 C++ 17 标准(在 C++ 14 标准中,要求指定复制范围的迭代器不是向量本身的迭代器),那么您可以使用插入方法,例如
#include <iostream>
#include <vector>
#include <iterator>
int main()
{
std::vector<int> v = { 1, 2, 3 };
v.insert( std::end( v ), std::begin( v ), std::end( v ) );
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
return 0;
}