使用条件运算符配合std::move操作shared_ptr出现异常行为的原因
核心问题:条件运算符的强制类型统一规则
C++里的?:条件运算符有个硬性要求:两个分支的表达式结果必须是同一类型(或者能隐式转换成同一个类型)。看你代码里的两个分支:
std::move(ptr)的类型是std::shared_ptr<int>&&(右值引用)std::shared_ptr<int>(ptr)是临时的std::shared_ptr<int>对象(右值)
编译器为了让两个分支类型统一,会把它们都转成std::shared_ptr<int>。对于std::move(ptr)这个右值引用,转换的方式就是调用shared_ptr的移动构造函数,生成一个临时的shared_ptr对象——这一步直接把ptr持有的资源偷走了,ptr当场就变成空的了。
也就是说,不止最后一次循环,前9次循环每次都会把ptr的资源移走。第一次循环结束后ptr已经是空指针了,后面的循环里拷贝构造出来的临时对象也都是空的,最后自然输出ptr is null。
if-else为啥不一样?
if-else是两个完全独立的语句块,没有强制类型统一的要求:
- 前9次循环:
std::shared_ptr<int>(ptr)是拷贝构造临时对象,ptr的引用计数临时加1,临时对象销毁后计数又减回去,ptr的资源从头到尾都没动过。 - 最后一次循环:
std::move(ptr)只是把ptr转成右值引用传给func的参数,但func里啥也没干——既没拿这个右值引用构造新对象,也没做移动赋值,所以ptr的资源根本没被窃取,自然还是有效的。
怎么让条件运算符和if-else行为一致?
只要让两个分支的类型统一成右值引用,避免编译器生成移动构造的临时对象就行:
func(i == 9 ? std::move(ptr) : std::move(std::shared_ptr<int>(ptr)));
这时两个分支都是std::shared_ptr<int>&&类型,不需要额外转换。前9次循环只会拷贝构造临时对象后再移动(但临时对象的资源会传给func,ptr不受影响),最后一次才会移动ptr,但func不操作资源,最终ptr仍然有效。
内容的提问来源于stack exchange,提问作者danry
相关产品推荐
相关产品推荐

