[英]Why does std::vector enforce copy on initialization?
I have a copy/move probing class: 我有一个复制/移动探测课:
#include <iostream>
struct A
{
A()
{
std::cout << "Creating A" << std::endl;
}
~A() noexcept
{
std::cout << "Deleting A" << std::endl;
}
A(const A &)
{
std::cout << "Copying A" << std::endl;
}
A(A &&) noexcept
{
std::cout << "Moving A" << std::endl;
}
A &operator=(const A &)
{
std::cout << "Copy-assigning A" << std::endl;
return *this;
}
A &operator=(A &&) noexcept
{
std::cout << "Move-assigning A" << std::endl;
return *this;
}
};
And I have found that running: 我发现跑步:
#include <vector>
int main(int, char **)
{
std::vector<A> v { A() };
}
Produces the following output: 产生以下输出:
Creating A
Copying A
Deleting A
Deleting A
Why won't the initialization just move the objects? 为什么初始化不会移动对象? I know that std::vector
may create undesired copies on resize , but as you can see, adding noexcept
did not help here (and besides, I don't think that the reasons a resize causes copies apply to initialization). 我知道std::vector
可能会在resize上创建不需要的副本 ,但正如你所看到的,添加noexcept
在这里没有帮助(此外,我不认为调整大小导致副本适用于初始化的原因)。
If I instead do the following: 如果我改为做以下事情:
std::vector<A> v;
v.push_back(A());
I don't get copies. 我没有复印件。
Tested with GCC 5.4 and Clang 3.8. 用GCC 5.4和Clang 3.8测试。
This isn't std::vector
, but std::initializer_list
. 这不是std::vector
,而是std::initializer_list
。
std::initializer_list
is backed by a const
array of elements. std::initializer_list
由const
数组元素支持。 It does not permit non- const
access to its data. 它不允许非const
访问其数据。
This blocks moving from its data. 这会阻止其数据移动。
But this is C++, so we can solve this: 但这是C ++,所以我们可以解决这个问题:
template<class T, class A=std::allocator<T>, class...Args>
std::vector<T,A> make_vector(Args&&...args) {
std::array<T, sizeof...(Args)> tmp = {{std::forward<Args>(args)...}};
std::vector<T,A> v{ std::make_move_iterator(tmp.begin()), std::make_move_iterator(tmp.end()) };
return v;
}
now we get: 现在我们得到:
auto v = make_vector<A>( A() );
gives you 1 extra move per element: 为每个元素提供1个额外的移动:
Creating A
Moving A
Moving A
Deleting A
Deleting A
Deleting A
We can eliminate that extra instance with a careful bit of reserving and emplacing back: 我们可以通过仔细保留和回放来消除这个额外的实例:
template<class T, class A=std::allocator<T>, class...Args>
std::vector<T,A> make_vector(Args&&...args) {
std::vector<T,A> v;
v.reserve(sizeof...(args));
using discard=int[];
(void)discard{0,(void(
v.emplace_back( std::forward<Args>(args) )
),0)...};
return v;
}
Live example of both -- simply swap v2::
for v1::
to see the first one in action. 两者的实例 - 简单地交换v2::
for v1::
以查看第一个实际操作。
Output: 输出:
Creating A
Moving A
Deleting A
Deleting A
there could be a bit more vector overhead here, as it may be hard for the compiler to prove that emplace_back
does not cause reallocation (even though we can prove it), so redundant checks will be compiled in most likely. 这里可能会有更多的向量开销,因为编译器可能很难证明emplace_back
不会导致重新分配(即使我们可以证明它),因此最有可能编译冗余检查。 (In my opinion, we need an emplace_back_unsafe
that is UB if there isn't enough capacity). (在我看来,如果没有足够的容量,我们需要一个emplace_back_unsafe
,即UB)。
The loss of the extra set of A
s is probably worth it. 失去额外的A
s可能是值得的。
Another choice: 另一个选择:
template<std::size_t N, class T, class A=std::allocator<T>, class...Args>
std::vector<T,A> make_vector(std::array<T, N> elements) {
std::vector<T,A> v{ std::make_move_iterator(elements.begin()), std::make_move_iterator(elements.end()) };
return v;
}
which is used like 用的像
auto v = make_vector<1,A>({{ A() }});
where you have to specify how many elements manually. 您必须手动指定多少元素。 It is as efficient as the version 2 above. 它与上面的版本2一样高效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.