[英]std::vector initialization move/copy constructor of the element
I have this piece of code:我有这段代码:
#include <iostream>
#include <vector>
using namespace std;
class Foo{
public:
Foo() noexcept {cout << "ctor" << endl;}
Foo(const Foo&) noexcept {cout << "copy ctor" << endl;}
Foo(Foo&&) noexcept {cout << "move ctor" << endl;}
Foo& operator=(Foo&&) noexcept {cout << "move assn" << endl; return *this;}
Foo& operator=(const Foo&) noexcept {cout << "copy assn" << endl; return *this;}
~Foo() noexcept {cout << "dtor" << endl;}
};
int main()
{
Foo foo;
vector<Foo> v;
v.push_back(std::move(foo));
// comment the above 2 lines and replace by
// vector<Foo> v{std::move(foo)};
}
The output is what I expect (compiled with g++ -std=c++11 --no-elide-constructors
, same output without the flag) output 是我所期望的(用
g++ -std=c++11 --no-elide-constructors
编译,相同的 output 没有标志)
ctor
move ctor
dtor
dtor
Now instead of using push_back
initialize directly the vector v
as现在不使用
push_back
而是直接将向量v
初始化为
vector<Foo> v{std::move(foo)};
I do not understand why I get the outputs:我不明白为什么我得到输出:
1) (without --no-elide-constructors
) 1) (没有
--no-elide-constructors
)
ctor
move ctor
copy ctor
dtor
dtor
dtor
2) (with --no-elide-constructors
) 2)(使用
--no-elide-constructors
)
ctor
move ctor
move ctor
copy ctor
dtor
dtor
dtor
dtor
In the first case, why is the copy ctor invoked?在第一种情况下,为什么要调用复制构造函数? And in the second case, when the compiler does not perform elision, I have absolutely no idea why the move ctor is invoked twice.
在第二种情况下,当编译器不执行省略时,我完全不知道为什么 move ctor 被调用两次。 Any ideas?
有任何想法吗?
vector<Foo> v{std::move(foo)};
Here you're calling the vector constructor that takes an std::initializer_list
.在这里,您正在调用带有
std::initializer_list
的向量构造std::initializer_list
。 An initializer list only allows const
access to its elements, so the vector
is going to have to copy each element from the initializer_list
to its own storage.初始值设定项列表只允许对其元素进行
const
访问,因此vector
必须将每个元素从initializer_list
复制到其自己的存储中。 That's what causes the call to the copy constructor.这就是导致调用复制构造函数的原因。
From §8.5.4/5 [dcl.init.list]来自§8.5.4/5 [dcl.init.list]
An object of type
std::initializer_list<E>
is constructed from an initializer list as if the implementation allocated a temporary array ofN
elements of typeconst E
, whereN
is the number of elements in the initializer list.std::initializer_list<E>
类型的对象是从初始化列表构造的,就好像实现分配了一个包含N
const E
类型元素的临时数组,其中N
是初始化列表中的元素数。
See alsohttps://tristanbrindle.com/posts/beware-copies-initializer-list另见https://tristanbrindle.com/posts/beware-copies-initializer-list
As for the extra move constructor call with -fno-elide-constructors
, this was discussed in another answer a couple of days ago.至于使用
-fno-elide-constructors
的额外移动构造函数调用,几天前在另一个答案中讨论过。 It seems as though g++ takes a very literal approach to the example implementation of an initializer_list
shown in the standard in same section I've quoted above.似乎 g++ 对我在上面引用的同一部分中的标准中显示的
initializer_list
的示例实现采用了一种非常直接的方法。
The same example, when compiled using clang , doesn't produce the extra move constructor call.同样的例子,当使用 clang 编译时,不会产生额外的移动构造函数调用。
The containers try very hard to make sure they remain usable should an exception occur.如果发生异常,容器会非常努力地确保它们仍然可用。 As part of this, they'll only use
std::move
internally if your class' move constructor is exception safe.作为其中的一部分,如果您的类的移动构造函数是异常安全的,他们只会
std::move
内部使用std::move
。 If it is not, (or it can't tell), it will copy just to be safe.如果不是,(或者它不能告诉),它会复制只是为了安全。
The correct move operations are正确的移动操作是
Foo(Foo&&) noexcept {cout << "move ctor" << endl;}
Foo& operator=(Foo&&) noexcept {cout << "move assn" << endl;}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.