函数的左值引用与右值引用有何区别?何时选用?
函数左值引用与右值引用的区别及适用场景
自C11起,存在左值引用(lvalue reference)与右值引用(rvalue reference),我了解二者的常规区别。但与
int(*fptr)()这类函数指针类似,C也支持函数的左值引用int(&lvalue)()与右值引用int(&&rvalue)(),但二者似乎并无差异。请问它们的区别是什么,应在何时分别选用?以下代码使用左值引用与右值引用执行相同操作,二者似乎可互换作为函数参数:
#include<cstdio> int add(int x){return x+1;} int runLvalue(int(&input)(int),int val){ return input(val); } int runRvalue(int(&&input)(int),int val){ return input(val); } int main() { int(&lval)(int)=add; int(&&rval)(int)=add; printf("%d %d %d %d %d %d", runLvalue(lval,8), runRvalue(lval,9), runLvalue(rval,0), runRvalue(rval,4), lval(5), rval(1)); return 1; }输出结果为
9 10 1 5 6 2,未体现出选用其中一种函数引用的理由。
你的代码里看不出区别,核心原因是函数本身是左值,且C++允许函数左值绑定到函数右值引用,所以无论是传递命名函数引用还是绑定了左值的右值引用,两个重载函数都能匹配成功。但二者在重载决议、模板编程等场景下存在明确区别,具体如下:
一、核心区别
1. 重载决议的匹配优先级
当存在接受函数左值引用和右值引用的重载函数时:
- 传递**命名函数(左值)**会优先匹配左值引用版本;
- 用
std::move()将函数转换为右值引用后,会匹配右值引用版本。
示例代码:
#include <cstdio> #include <utility> void func() {} void overload(void(&)()) { printf("匹配左值引用重载\n"); } void overload(void(&&)()) { printf("匹配右值引用重载\n"); } int main() { overload(func); // 输出:匹配左值引用重载 overload(std::move(func));// 输出:匹配右值引用重载 return 0; }
2. 模板推导与完美转发
在模板中,函数右值引用结合模板参数可实现万能引用(F&&),既能接受函数左值,也能接受函数右值引用,配合std::forward可完美转发函数的原始值类别;而函数左值引用模板只能接受左值。
示例代码:
#include <cstdio> #include <utility> void target(int x) { printf("target: %d\n", x); } template<typename F, typename... Args> auto forward_func(F&& f, Args&&... args) -> decltype(f(std::forward<Args>(args)...)) { return f(std::forward<Args>(args)...); } int main() { auto& lfunc = target; forward_func(lfunc, 10); // 转发左值函数引用 forward_func(std::move(target), 20); // 转发右值函数引用 return 0; }
3. 语义区分
- 函数左值引用通常表示要操作一个持久存在的函数,强调引用的稳定性;
- 函数右值引用可用来标记临时的函数引用,语义上暗示该引用不需要长期持有(虽然函数本身无法被移动,但可用于区分传递意图)。
二、适用场景
- 当需要限制参数只能是命名的函数左值时,使用函数左值引用;
- 当需要在重载中区分左值/右值函数引用,或实现函数引用的完美转发时,使用函数右值引用;
- 模板编程中,万能引用(
F&&)是处理函数引用转发的首选方式,能同时兼容左值和右值场景。
内容的提问来源于stack exchange,提问作者LoquaciousLlama
相关产品推荐
相关产品推荐

