拷贝构造/赋值函数的参数: 为什么我的拷贝函数报错?#
拷贝函数用 Widget(Widget const& other)
和 operator=(Widget const& other)
.
本解答是很多人用 &
作为参数出错而提问, 我忍无可忍的结果.
为什么不是 Widget(Widget other)
和 operator=(Widget other)
?#
定义拷贝构造函数的目的是什么? 是为了描述 Widget
对象如何进行拷贝.
当用 Widget(Widget other)
时, 我们就是在说, Widget
先进行拷贝传参, 传入参数后进行拷贝. 拷贝传参也是在拷贝 Widget
, 所以我们要为了拷贝传参这次拷贝进行拷贝传参, 传入参数后进行拷贝……
从前有座山, 山里有座庙, 庙里有个老和尚在讲故事, 讲的是从前有座山, 山里有座庙, 庙里有个老和尚在讲故事……
发生无限的递归.

Widget(Widget other)
带来的递归问题. (以前做的图, 用语不太一样)#
为什么不是 Widget(Widget& other)
和 operator=(Widget& other)
?#
拷贝只是读取数据, 不会修改 other
#
按照拷贝的逻辑, 我们是从 other
中读取数据, 它本身的数据不应该发生任何变化, 为了避免被修改, 应该使用 Widget const&
传递参数. 将 other
作为只读参数, 这称为输入参数.
Widget&
被意外修改# 1class Widget {
2 public:
3 Widget(Widget& other) {
4 /*...*/
5 --other.value_; // 意外被修改!
6 }
7
8 private:
9 int value_;
10};
Widget&
只能引用左值, 但很多情况需要用右值拷贝#
前置内容
Widget&
报错# 1class Widget {
2 public:
3 Widget(Widget& other);
4};
5
6Widget function();
7
8int main() {
9 Widget widget = function(); // 错误: function 返回了一个临时对象, 不能被引用而用于拷贝!
10}
Widget const&
正常运行# 1class Widget {
2 public:
3 Widget(Widget const& other);
4};
5
6Widget function();
7
8int main() {
9 Widget widget = function(); // 正确!
10}
危险
以上内容是针对 C++98~C++23 的泛用解答, 随着版本变化, 很多细节会有差异.
事实上很多解答都存在这样的问题, 因为教学使用的 C++98 太过时了, VS 2017 默认版本是 C++14, gcc 11 和 clang 16 默认版本是 C++17.
这里引起的争议比较大, 所以免责声明一下.