【问题标题】:Why cast expression to rvalue reference to function is lvalue?为什么将表达式转换为右值对函数的引用是左值?
【发布时间】:2015-03-18 07:57:28
【问题描述】:

在这里,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;
}

事实如此。

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

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

【问题讨论】:

  • 我认为技术上的答案是因为标准在 [conv] / 6、[expr.static.cast] / 1 等中这么说。
  • 一个很好的理由是here(在 cmets 中有额外的说明)

标签: c++ c++11


【解决方案1】:

它遵循 C++11 3.10/1 中值类别的定义(强调我的):

  • lvalue (...) 指定 函数 或对象。 ...

  • 一个xvalue(一个“eXpiring”值)也指一个对象,...

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

  • prvalue(“纯”右值)是一个不是 xvalue 的右值。 ...

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

在 5.2.9/1 中也有呼应:

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

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

【讨论】:

  • N3055有一些讨论。
【解决方案2】:

非常感谢 T.C. 提供的信息(N3055,link at his comment)。 ,为了方便快速浏览答案,我引用了一些与这个问题相关的段落。这个答案告诉我们原因

右值引用(像传统的左值引用)可以绑定到 职能。 将右值引用返回值视为右值, 然而,将函数右值的新概念引入 以前没有这样的想法 - 使用函数 lvalue 在右值上下文中成为指向函数右值的指针,而不是 函数右值——所以当前的标准草案没有描述如何 这样的右值将被处理。 尤其是函数调用和 函数指针的转换是根据函数指定的 左值, 所以对函数的右值引用最合理的用法是 在当前措辞中未定义。

一种可能的解决方案 问题将是维持目前的治疗方法 右值引用返回值作为右值但添加各种警告 到右值的规范,以便那些来自右值的 引用将具有特殊的特征。这可以称为 “有趣的右值”方法。然而,进一步审查 标准草案的当前措辞表明存在的问题 上面列出的可能只是冰山一角:许多 应该适用于右值对象的规范 引用引用,例如对象生存期、别名规则等,是 用左值来表达,所以右值警告列表可以得到 很长

这提出了另一种方法:右值引用返回 值实际上应该被视为左值,除了少数例外 在这种情况下,允许它们被视为右值 预期的,即在引用绑定、重载决议和 模板参数推导。这个想法,被称为“有趣的左值” 方法,体现在本文的早期版本中。经过核心工作组在匹兹堡(2010 年 3 月 8 日至 13 日)会议上的广泛讨论, ......

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

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

现在static_cast&lt;void(&amp;&amp;)(int)&gt;(test) 是一个左值。

所以static_cast&lt;void(&amp;&amp;)(int)&gt;(test)(555); 没问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多