为什么将表达式强制转换为对函数的右值引用是左值

Why cast expression to rvalue reference to function is lvalue?

本文关键字:引用 函数 表达式 转换 为什么      更新时间:2023-10-16

在这里,cppreference lvalue,我发现

将表达式强制转换为右值,对函数的引用为左值。

我很好奇,所以我进行了以下实验:

#include <iostream>
using namespace std;

typedef void (&&funr) (int);
typedef void (&funl) (int);
void test(int num){
    cout<<num<<endl;//output:20
}
void foo(funr fun){
    fun(10);
}
void foo(funl fun){
    fun(20);//call this
}
template <typename T> void foo(T&& fun){
    cout<<is_same<T,void(&)(int)>::value<<endl;//true, it is lvalue. 
    cout<<is_same<T,void(int)>::value<<endl;//false, it isn't rvalue.
}
int main()
{
   foo(static_cast<void(&&)(int)>(test));
   return 0;
}

事实上是这样。

为什么将表达式强制转换为对函数的右值引用是左值?是因为函数类型不需要移动语义还是其他什么?或者我把这个词理解错了。

将表达式强制转换为函数的右值引用为左值

它源于C++11 3.10/1(emphasis mine)中值类别的定义:

  • 左值(…)指定函数或对象。。。

  • xvalue("eXpiring"值)也指对象。。。

  • 右值(…)是一个xvalue、一个临时对象(12.2)或其子对象,或者是一个与对象无关的值。

  • prvalue("纯"右值)是一个不是xvalue的右值。。。

请注意,只有左值类别可以是函数,其他所有类别都只能是值或对象。

它也在5.2.9/1:中得到了回应

表达式static_cast<T>(v)的结果是将表达式v转换为类型T的结果如果T左值引用类型或函数类型的右值引用,则结果为左值;如果T是右值引用对象类型,结果是一个xvalue;否则,结果为prvalue。。。

至于原因,我当然只能猜测(不是标准化委员会的成员)。但我的猜测是,拥有函数类型的右值是没有意义的;函数永远不可能是临时的,它永远不可能处于或接近其生命周期的尽头。

非常感谢T.C.提供的信息(N3055,其评论处的链接),以便快速概述答案,我引用了与此问题相关的一些段落这个答案告诉我们为什么

右值引用(像传统的左值引用)可以绑定到功能将右值参考返回值视为右值,然而,在语言以前没有这样的想法——使用了一个函数lvalue在右值上下文中,它变为指向函数右值的指针,而不是函数值–因此当前的标准草案没有描述如何这样的右耳值将被处理特别是函数调用和函数指针的转换是根据函数指定的左值,所以对函数的右值引用的最合理的用法是当前措辞中未定义

一个可能的解决方案问题是要保持目前治疗右值引用返回值作为右值,但要添加各种注意事项对右耳值的规范,使那些来自右耳值参考文献将具有特殊的特征。这可以称为"滑稽的右耳值"方法。然而,对标准草案目前的措辞表明上面列出的可能只是冰山一角:应应用于右值所指向的对象的规范引用引用,如对象生存期、别名规则等,是用左值表示,因此右值警告列表可以得到相当长

这提出了另一种方法:重新值引用返回值实际上应该被视为lvalues,只有少数例外允许它们被视为右价预期,即参考绑定、过载解决,以及模板参数推导。这个想法被称为"有趣的左值"方法,体现在本文的早期版本中。在匹兹堡(2010年3月8日至13日)会议上核心工作组进行了广泛讨论之后,……

此外,正如5.2.2函数调用[expr.call]所说:

对于对非成员函数或静态成员函数的调用,后缀表达式应为引用函数(在这种情况下,函数到指针的标准转换(4.3)在后缀表达式上被抑制),或者它应该具有指向函数类型的指针。

现在static_cast<void(&&)(int)>(test)是一个左值。

所以static_cast<void(&&)(int)>(test)(555);是可以的。