gpt4 book ai didi

c++ - 我在解释 N4140 的§8.5.3/5 段中的要点 (5.2.1.1) 时遇到了一些困难

转载 作者:太空狗 更新时间:2023-10-29 20:39:24 24 4
gpt4 key购买 nike

下面的代码片段编译

#include <iostream>
int& f() { static int i = 100; std::cout << i << '\n'; return i; }

int main()
{
int& r = f();
r = 101;
f();
}

并打印值(live example)

100
101

现在,阅读 N4140 中的 §8.5.3/5,我可以看到它编译是因为要点 (5.1.1),即引用是左值引用,初始化表达式是左值和 intint(或 int& - 我不确定我应该在这里使用哪个)引用兼容。

要点 (5.1) 和 (5.1.1):

— If the reference is an lvalue reference and the initializer expression

 — is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with
“cv2 T2,” or ...

现在假设我将声明 int& r = f(); 中的左值引用更改为右值引用,即 int&& r = f(); .我知道代码不会编译,因为右值引用不会绑定(bind)到左值。但我很好奇的是,如何使用标准得出这个结论?

我会解释我的困难是什么:

  1. 显然 int&& r = f(); 包含在要点 (5.2) 中,因为引用是右值引用。

要点 (5.2):

— Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.

  1. 原则上,我会说 (5.2.1.1) 支持这种初始化,因为初始化器是一个函数左值,intint(或 int&).

要点 (5.2.1) 和 (5.2.1.1):

— If the initializer expression

— is an xvalue (but not a bit-field), class prvalue, array prvalue or function
lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or ...

编辑

我逐字包含了 N4140 (C++14) 中的要点,它们等同于 N3337 (C++11) 中的类似要点。

最佳答案

the initializer expression is an lvalue and int is reference-compatible with int (or with int& - I don't know for sure which one I should use here).

引用兼容性是应用于引用类型的关系,而不是引用类型。例如,[dcl.init.ref]/5 谈到通过类型 cv2 T1 的表达式初始化“对类型 cv1 T2 的引用”,以及后来比较例如“其中 T1T2 不相关”。

表达式的类型 f()只是int ,尽管 f 的返回类型是int& .当我们观察它们时,表达式根本没有引用类型(*);引用被剥离并用于确定值类别(见 [expr]/5)。对于 int& f() , 表达式 f()是左值;对于 int g() , 表达式 g()是右值。

(*)准确地说,表达式 can have reference type in the Standard ,但仅作为“初始”结果类型。该引用“在任何进一步分析之前”被删除,这意味着该引用根本无法通过该类型观察到。


Now suppose I change the left value reference in the declaration int& r = f(); by a right value reference, i.e., int&& r = f();. I know the code won't compile, as an rvalue reference doesn't bind to an lvalue. But what I'm curious is, how to reach this conclusion using the Standard?

从评论中的讨论来看,困惑似乎是 f()不是函数左值。 “左值”和“右值”等值类别是表达式的属性。因此,术语“函数左值”必须指代一个表达式,即 具有值类别“左值” 的函数类型表达式。

但是表达式 f()是一个函数调用表达式。从语法上讲,它是一个后缀表达式,后缀是函数参数列表。根据 [expr.call]/10:

A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.

和 [expr.call]/3

If the postfix-expression designates a destructor [...]; otherwise, the type of the function call expression is the return type of the statically chosen function [...]

也就是说,表达式的(观察到的见上文)类型f()int ,值类别为“左值”。请注意,(观察到的)类型不是 int& .

函数左值例如是一个id-expression,如f ,间接函数指针的结果,或产生任何类型的函数引用的表达式:

using ft = void();
void f();
ft& l();
ft&& r();
ft* p();

// function lvalue expressions:
f
l()
r()
*p()

[expr.prim.general]/8 指定那些标识符,如 f作为id-expressions,左值是:

An identifier is an id-expression provided it has been suitably declared. [...] The type of the expression is the type of the identifier. The result is the entity denoted by the identifier. The result is an lvalue if the entity is a function, variable, or data member and a prvalue otherwise.


回到例子int&& r = f(); .使用一些 N4296 之后的草稿。

[dcl.init.ref]

5 A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

  • (5.1) If the reference is an lvalue reference and the initializer expression

引用是一个右值引用。 5.1 不适用。

  • (5.2) Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference. [example omitted]

这适用,引用是右值引用。

  • (5.2.1) If the initializer expression
    • (5.2.1.1) is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and [...], or
    • (5.2.1.2) has a class type (i.e., T2 is a class type) [...]

初始化器是一个 int 类型的左值. 5.2.1 不适用。

  • (5.2.2) Otherwise:
    • (5.2.2.1) If T1 or T2 is a class type [...]
    • (5.2.2.2) Otherwise, a temporary of type “cv1 T1” is created and copy-initialized (dcl.init) from the initializer expression. The reference is then bound to the temporary.

最后,5.2.2.2 适用。然而:

If T1 is reference-related to T2:

  • (5.2.2.3) cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2; and
  • (5.2.2.4) if the reference is an rvalue reference, the initializer expression shall not be an lvalue.

T1T2int (f() 的返回类型的引用被删除,仅用于确定值类别),因此它们是引用相关的。 cv1cv2 都是空的。 引用是右值引用,f()是左值,因此 5.2.2.4 使程序格式错误。


术语“函数左值”出现在 5.2.1.1 中的原因可能与“函数右值”问题有关(例如,参见 N3010 - Rvalue References as "Funny" Lvalues)。 C++03 中没有函数右值,委员会似乎不想在 C++11 中引入它们。没有右值引用,我认为不可能获得函数右值。例如,您不能转换为函数类型,也不能从函数返回函数类型。

可能为了保持一致性,函数左值可以通过强制转换绑定(bind)到函数类型的右值引用:

template<typename T>
void move_and_do(T& t)
{
T&& r = static_cast<T&&>(t); // as if moved
}

int i = 42;
move_and_do(i);

move_and_do(f);

但对于 T是像 void() 这样的函数类型, static_cast<T&&>(t) 的值类别是左值(没有函数类型的右值)。因此,对函数类型的右值引用可以绑定(bind)到函数左值。

关于c++ - 我在解释 N4140 的§8.5.3/5 段中的要点 (5.2.1.1) 时遇到了一些困难,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27840925/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com