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

模板中左值(&)/右值(&&)引用绑定与引用折叠规则及案例解析

C++左值/右值引用绑定与引用折叠规则详解及案例分析

核心规则

1. 引用绑定基础规则

  • 左值引用(T&):仅可绑定到可修改的左值(如命名变量、返回左值引用的表达式),无法直接绑定右值(临时对象、字面量等);const T&是例外,可绑定任意左/右值,但本文案例均为非const引用。
  • 右值引用(T&&):仅可绑定到右值;但如果是函数模板参数推导语境下的TYPE&&,则为万能引用——会根据传入实参的类型推导TYPE,再通过引用折叠确定最终引用类型。

2. 引用折叠规则

所有引用组合的折叠结果遵循“左值优先”原则:只要参与折叠的引用中有一个是左值引用,结果即为左值引用;仅当两个都是右值引用时,结果才是右值引用:

  • T& & → T&
  • T& && → T&
  • T&& & → T&
  • T&& && → T&&

案例问题拆解

问题1:为何B b2{int(1)}(case4)合法,而case2、5、6、7不合法?

逐个分析相关案例:

case5:int& ii{int(12)};(Error)

直接用临时右值初始化非const左值引用,违反左值引用绑定规则,编译报错。

case6:int& iii{std::move(int(12))};(Error)

std::move(int(12))仅将临时右值转换为右值引用类型,但本质仍为右值,非const左值引用无法绑定右值,编译报错。

case2:A a2{int(1)};(标注OK)

A的构造函数参数int&& pi绑定到临时右值int(1),成员int& i绑定的是pi——所有命名的引用变量都是左值,因此i绑定左值pi是合法的。注意:pi引用的临时对象会在构造函数执行完毕后销毁,a2.i会成为悬垂引用,属于运行时未定义行为,但编译阶段仅检查绑定规则,因此标注为OK。

case7:C c1{i};(Error)

C是类模板,C c1{i}会推导类模板参数TYPE为int,此时构造函数参数为int&& pi——这是普通右值引用,而非万能引用(万能引用仅存在于函数模板参数推导语境,类模板参数确定后,函数参数类型固定)。左值i无法绑定到右值引用int&&,编译报错。

case4:B b2{int(1)};(OK)

B的构造函数是函数模板:template<typename TYPE> B(TYPE&& pi),这里的TYPE&&是万能引用。传入右值int(1)时,推导TYPE为int,TYPE&&折叠为int&&,pi绑定到临时右值。成员int& i绑定到pi(命名右值引用是左值),符合左值引用绑定规则,编译合法。

问题2:为何case9合法,而case10、11不合法?

case9:C<int&> c3{i};(OK)

显式指定类模板参数TYPE=int&,构造函数参数TYPE&& pi即int& && pi,根据引用折叠规则,int& && → int&,因此构造函数参数实际为int& pi,可绑定左值i。成员TYPE& i即int& & i,折叠为int& i,绑定到左值pi,符合规则。

case10:C<int&> c4{int(1)};(Error)

显式指定TYPE=int&后,构造函数参数折叠为int& pi,需要绑定左值,但传入的int(1)是右值,左值引用无法绑定右值,编译报错。

case11:int&& iiii{ir};(Error)

ir是左值引用,本身属于左值;右值引用int&&仅可绑定右值,无法绑定左值,因此编译报错。此处无模板推导或引用折叠,仅为普通右值引用的绑定规则问题。


关键区分:万能引用与普通右值引用

万能引用的核心判定条件:

  • 仅出现在函数模板参数推导语境中;
  • &&所依附的类型参数是被推导的未限定类型(如template<typename T> void func(T&&))。
    若&&是类模板参数的一部分(如C(TYPE&& pi)中TYPE是类模板参数),则为普通右值引用(或折叠后的引用),无参数推导过程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 09:15:03