请教下关于拷贝构造函数的问题

2022-07-20 00:53:22 +08:00
 frankmdong

最近在看 Professional C++, Fifth Edition 入门,看到 rule of five 和移动构造函数的时候写了个示例测了下,发现一个奇怪的问题。

为什么 std::vector<Data> myDatas{data}; 这行会调两次 Data 的拷贝构造函数呢?是复制初始化导致的吗?

如果我改成 std::vector<Data> myDatas; 然后 push_back(data), 这里就只会打印一次 "Copy constructor"。

另外:C++ 一般这种问题应该怎么分析?一般是看汇编还是断点?

int main() {
    DataHolder wrapper;
    Data data{11};                  // Normal constructor
    std::vector<Data> myDatas{data};// Copy constructor * 2
    wrapper.setDatas(myDatas);      // Copy constructor
}

全代码在:https://godbolt.org/z/EE5vnG3Kz

菜鸟问题有点多,不好意思

1571 次点击
所在节点    C++
8 条回复
pocarisweat
2022-07-20 01:16:22 +08:00
https://stackoverflow.com/questions/20501638/stdvector-init-with-braces-call-copy-constructor-twice

因为 std::vector { data } 实际上调用的是 std::initializer_list 版本的构造函数,所以从 data 到 initializer_list copy 了一次,而从 initializer_list 到 vector 又 copy 了一次。如果写成 std::vector { std::move(data) } 会发现 move 了一次 copy 了一次。

如果你写成 std::vector(1, data) 就会只 copy 一次。
pocarisweat
2022-07-20 01:19:42 +08:00
@pocarisweat
不过这里是因为 Data 的复制构造函数有副作用,所以按照语言标准必须调用两次。如果没有副作用,编译器后端很容易(但不保证)将其优化掉。这有点类似物理学的不确定性原理🐶
frankmdong
2022-07-20 11:07:13 +08:00
@pocarisweat #2 #2 感谢回复!
“Data 的复制构造函数有副作用” 中的副作用是指我自定义了复制构造函数吗?
在 godbolt 里面还有一处是

DataHolder wrapper;
std::vector<Data> myDatas{11}; // 11 是隐式构造 Data{11}
wrapper.setDatas(myDatas);

这样写的话,std::initializer_list 那里又变成只有一次 Copy constructor 了,感觉就是...很迷...
cnbatch
2022-07-20 14:10:35 +08:00
也许这样描述会清楚一点


std::vector<Data> myDatas { data }

第一次 Copy constructor:data“塞入”std::initializer_list{}
第二次 Copy constructor:从 std::initializer_list{} “变成” std::vector<Data>



std::vector<Data> myDatas{11}

第一次 Copy constructor:11“塞入”std::initializer_list{}。数字 11 此时不是 class Data ,仅仅是简单的基本类型复制,跟那个 class 暂时还没关联,并不会用到你在 class Data 提供的 Copy constructor 。
第二次 Copy constructor:从 std::initializer_list{} “变成” std::vector<Data>
frankmdong
2022-07-20 14:23:23 +08:00
@cnbatch #4 #4 感谢回复!这个塞入 std::initializer_list{} 的区别是不是就是左值和右值的区别? std::vector<Data> myDatas{Data{11}},也只会打印一次 Copy constructor 。 而一开始传的是个 data 变量,这是个左值,所以会复制多一遍。
cnbatch
2022-07-20 14:42:30 +08:00
@frankmdong 没错,就是左值和右值的缘故
pocarisweat
2022-07-21 00:03:57 +08:00
@frankmdong
这里的副作用指的是调用了外部函数来 print ,如果没有这个外部调用,编译器能很容易知道它啥也没做然后就能优化掉。
frankmdong
2022-07-21 00:44:29 +08:00
@pocarisweat #7 #7 懂啦,看来老哥只在凌晨刷 v 站

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/867406

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX