函数形参类型与返回值类型
参考:
- https://shaharmike.com/cpp/rvo/
- https://www.zhihu.com/question/22941176
- https://www.zhihu.com/question/534389744
注意,不探讨 const
1. 函数形参
1.1 实参、形参组合
我们知道 c++ 中函数形参可以有如下几种:
- 左值
- 左值引用
- 右值引用
而实参也可以有如下几种:
- 左值/左值引用
- 右值
将他们组合起来,形参有如下情况:
- 形参为左值:
- 实参为左值/左值引用,那么会发生拷贝构造行为,语义上可以看作资源被复制了一份
- 实参为右值,那么会发生移动构造行为,语义上可以看作资源发生了转移,caller 不再拥有实参的资源所有权
- 形参为左值引用:
- 实参为左值/左值引用,不会发生任何构造行为,语义上可以看作资源被共享了一份
- 实参为右值,语法错误
- 形参为右值引用:
- 实参为左值/左值引用,语法错误
- 实参为右值,也不会发生任何构造行为,语义上可以看作临时资源被共享了一份。但是实际上,一般右值引用作为形参的函数都是 callee 在函数内部进行移动构造,以转移资源所有权
1.2 智能指针作为形参
智能指针作为形参时,形参类型为左值、右值引用、左值引用都可以。
但是考虑到智能指针是带有引用计数功能的类,从智能指针的设计语义出发,形参为左值引用应该被排除。形参为右值引用意味着 caller 要转移对象资源,由于智能指针类都设计有移动构造(unique_ptr、shared_ptr),所以直接用左值接受即可。
可以参考如下设计规则(https://www.zhihu.com/question/534389744/answer/2500052393):
unique_ptr<widget> factory(); // 生产 widget
void sink(unique_ptr<widget>); // 消费 widget
void reseat(unique_ptr<widget>&); // 将或可能修改指针
void thinko(const unique_ptr<widget>&); // 通常不是你想要的
shared_ptr<widget> factory(); // 确知生产的结果需要共享
void share(shared_ptr<widget>); // 将保留引用计数
void reseat(shared_ptr<widget>&); // 将或可能修改指针
void may_share(const shared_ptr<widget>&); // 可能保留引用计数
2. 函数返回值
我们知道 c++ 中函数返回值可以有如下几种:
- 左值(如果关闭返回值优化特性,实际上对 caller 来说返回的是一个临时对象,一个右值)
- 左值引用
- 右值引用
函数返回的值本身,可以看作有如下属性:
- 非局部对象
- 局部对象
2.1 返回非局部对象
2.1.1 不转移对象所有权(返回左值或左值引用)
如果返回:
- 左值,caller 不能采用左值引用进行接收(对于 caller 来说,返回的左值语法上是个临时对象,值属性为右值),触发拷贝构造/赋值构造
- 左值引用,caller 可以采用左值引用进行接收。也可以采用左值进行接收,触发拷贝构造/赋值构造
2.1.2 转移对象所有权(返回前调用 std::move(),返回形参左值或右值引用)
如果返回:
- 左值,触发移动构造/移动赋值构造
- 右值引用,也会触发移动构造/移动赋值构造
2.2 返回局部对象
局部对象返回左值即可,使用左值接收,以触发返回值优化。如果返回引用,无论左值引用还是右值引用,caller 接收对象都是未定义行为