You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

函数调用中拷贝构造函数为何延迟调用?

万能引用函数模板与普通引用函数的行为差异解析

结论

这些行为完全符合C++标准的预期,核心差异来自万能引用的类型推导与引用折叠规则。

Foo模板的行为拆解

当调用Foo(x)时,x是一个非const左值,根据C++的万能引用推导规则:

  • 万能引用T&&遇到左值时,T会被推导为对应的左值引用类型——这里T推导为X&(引用折叠规则:X& && 最终等价于 X&)
  • 此时Foo被实例化为以下代码:
X& Foo(X& t) {
  X& out{std::forward<X&>(t)}; // out是t的引用,也就是main函数中x的别名
  std::cout << "out = " << out.s_ << std::endl;
  return out;
}

这就是为什么输出out = name:out并不是新构造的X对象,它只是main里x的引用,访问out.s_自然就是x的s_值。

当return out时,返回的是X&类型,main中auto y = Foo(x)会将该引用指向的x拷贝构造为y,因此触发拷贝构造函数X(const X&),y的s_被设置为copy_con_val,对应输出中的X(const X&)和最终的all done: y = copy_con_val。

Bar普通函数的行为对比

Bar是普通函数,参数明确为const X&,函数内的X out{x};会直接调用拷贝构造函数创建一个新的X对象,out的s_被初始化为copy_con_val,因此输出out = copy_con_val。后续返回out时即便触发返回值优化(RVO),也只是直接在main的y内存上构造对象,不影响out本身是新对象的事实,所以输出符合预期。

如何让Foo实现预期行为

如果你希望Foo中构造新的X对象,需要将返回值和局部变量的类型改为移除引用后的T,可以用std::remove_reference_t实现:

#include <type_traits> // 需要包含头文件

template <typename T>
std::remove_reference_t<T> Foo(T&& t) {
  std::remove_reference_t<T> out{std::forward<T>(t)};
  std::cout << "out = " << out.s_ << std::endl;
  return out;
}

此时调用Foo(x)会实例化为返回X类型,out是新构造的对象,会输出out = copy_con_val,同时返回时的RVO可以避免额外拷贝。

内容的提问来源于stack exchange,提问作者Stephen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 10:10:20