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

为何使用auto时C++的+运算符默认匹配右值重载?

问题分析

先整理你的测试代码:

#include <iostream>
#include <cstdio>

struct Obj{ Obj(){} };
struct Obj2{ Obj2(){} };

// option 1
Obj operator+(Obj const& x0, Obj const& x1) { printf("%s\n", "+(const&, const&)"); return Obj{}; }
Obj operator+(Obj const& x0, Obj&& x1) { printf("%s\n", "+(const&, &&)"); return Obj{}; }
Obj operator+(Obj&& x0, Obj const& x1) { printf("%s\n", "+(&&, const&)"); return Obj{}; }
Obj operator+(Obj&& x0, Obj&& x1) { printf("%s\n", "+(&&, &&)"); return Obj{}; }

//option 2
// Obj2 operator+(auto const& x0, auto const& x1) { printf("%s\n", "+(const&, const&)"); return Obj2{}; }
// Obj2 operator+(auto const& x0, auto&& x1) { printf("%s\n", "+(const&, &&)"); return Obj2{}; }
// Obj2 operator+(auto&& x0, auto const& x1) { printf("%s\n", "+(&&, const&)"); return Obj2{}; }
// Obj2 operator+(auto&& x0, auto&& x1) { printf("%s\n", "+(&&, &&)"); return Obj2{}; }

int main()
{
    Obj x0;
    Obj x1{ x0 };
    auto x2 = x0 + x1;
}

你遇到的现象可以拆成三个核心点解释:


1. Option1 选 const&, const& 的原因

Option1 没有提供针对非const左值引用的operator+(Obj&, Obj&)重载,而x0、x1都是非const左值。在重载决议中,非const左值可以直接绑定到const&参数,其他重载需要把左值强制转成右值(比如std::move(x0)),这会引入额外转换,优先级更低。所以编译器只能选最匹配的const&, const&版本。


2. Option2 看似选 &&, && 的本质

这是两个细节共同导致的:

(1)转发引用的推导规则

模板参数里的auto&&是转发引用(不是普通右值引用),推导逻辑是:

  • 传入左值时,auto&&会被推导为左值引用:比如auto&& x0推导为Obj& &&,根据C++引用折叠规则,最终变成Obj&;
  • 只有传入右值时,才会推导为右值引用Obj&&。

(2)模板重载的匹配优先级

启用Option2后,传入两个非const左值时,四个模板都会被实例化:

  • 第一个模板实例化为:Obj2 operator+(Obj const&, Obj const&)
  • 第二个模板实例化为:Obj2 operator+(Obj const&, Obj&)
  • 第三个模板实例化为:Obj2 operator+(Obj&, Obj const&)
  • 第四个模板实例化为:Obj2 operator+(Obj&, Obj&)

在重载决议中,非const左值引用比const左值引用更匹配非const左值,所以第四个模板实例化出的Obj&, Obj&版本是最精确的匹配,编译器会优先选择它。

(3)打印字符串的误导

你写的第四个模板里,printf的字符串是固定的"+(&&, &&)",但实际上这个模板实例化后的参数是Obj&, Obj&(左值引用),并不是右值引用。只是打印内容让你误以为编译器把x0、x1识别成了右值,实际上它们还是左值。


3. 为什么不优先选const&, const&版本

这里的“约束性”和模板重载的优先级逻辑无关:普通函数重载里const&是更宽松的(能接受更多类型),但模板实例化后的版本中,Obj&, Obj&比Obj const&, Obj const&更精确匹配非const左值实参,所以编译器会选更精确的那个。

简单说:Option1里没有更精确的非const左值引用重载,只能退而求其次选const&;Option2的模板会自动生成最精确的匹配版本,所以选了它。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 05:16:00