为何使用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

