为什么第一个返回引用?
int x = 1;
int y = 2;
(x > y ? x : y) = 100;
而第二个没有?
int x = 1;
long y = 2;
(x > y ? x : y) = 100;
实际上,第二个根本没有编译 - “不是左值赋值”。
为什么第一个返回引用?
int x = 1;
int y = 2;
(x > y ? x : y) = 100;
而第二个没有?
int x = 1;
long y = 2;
(x > y ? x : y) = 100;
实际上,第二个根本没有编译 - “不是左值赋值”。
表达式没有返回类型,它们有一个类型和 - 正如最新的 C++ 标准中所知道的 - 一个值类别。
条件表达式可以是左值或右值。这是它的价值范畴。(这有点简化,C++11
因为我们有左值、xvalues 和 prvalues。)
在非常广泛和简单的术语中,左值指的是内存中的对象,而右值只是一个可能不一定附加到内存中的对象的值。
赋值表达式为对象赋值,因此被赋值的事物必须是左值。
要使条件表达式 ( ?:
) 成为左值(同样,从广义和简单的角度来说),第二个和第三个操作数必须是相同类型的左值。这是因为条件表达式的类型和值类别是在编译时确定的,并且无论条件是否为真都必须是适当的。如果必须将其中一个操作数转换为不同的类型以匹配另一个,则条件表达式不能是左值,因为此转换的结果不会是左值。
ISO/IEC 14882:2011 参考:
3.10 [basic.lval] 左值和右值(关于值类别)
5.15 [expr.cond] 条件运算符(条件表达式具有什么类型和值类别的规则)
5.17 [expr.ass] 赋值和复合赋值运算符(要求赋值的 lhs 必须是可修改的左值)
三元?:
表达式的类型是它的第二个和第三个参数的通用类型。如果两种类型相同,您将获得一个参考。如果它们可以相互转换,则选择一个并转换另一个(在这种情况下提升)。由于您不能返回对临时(转换/提升的变量)的左值引用,因此它的类型是值类型。
它不能返回左值,因为它必须隐式提升的类型x
以匹配的类型y
(因为两边的:
类型不同),并且必须创建一个临时值。
表达式 5.17 赋值和复合赋值运算符
5.17/3
如果第二个和第三个操作数具有不同的类型,并且其中一个具有(可能是 cv 限定的)类类型,则尝试将这些操作数中的每一个转换为另一个的类型。确定类型 T1 的操作数表达式 E1 是否可以转换为匹配类型 T2 的操作数表达式 E2 的过程定义如下:
— 如果 E2 是左值:如果 E1 可以隐式转换(第 4 条)为“对 T2 的引用”类型,则可以将 E1 转换为匹配 E2,但必须遵守在转换中引用必须直接绑定的约束(8.5.3 ) 到 E1。
— 如果 E2 是右值,或者无法进行上述转换:
— 如果 E1 和 E2 具有类类型,并且基础类类型相同或其中一个是另一个的基类:如果 T2 的类的类型相同,或者是,T1 的类别,并且 T2 的 cv 限定与 T1 的 cv 限定相同或更高的 cv 限定。如果应用了转换,则 E1 将更改为 T2 类型的右值,该右值仍引用原始源类对象(或其适当的子对象)。[注:即不复制。— 结束注释] 通过从 E1 复制初始化 T2 类型的临时变量并将该临时变量用作转换后的操作数。
否则(即,如果
E1
E2 或 E2 具有非类类型,或者如果它们都具有类类型但基础类不同或不是另一个的基类):如果 E1 可以是,则 E1 可以转换为匹配 E2如果 E2 被转换为右值,则隐式转换为表达式 E2 将具有的类型(或如果 E2 是右值,则它具有的类型)。使用这个过程,确定是否可以转换第二个操作数以匹配第三个操作数,以及是否可以转换第三个操作数以匹配第二个操作数。如果两者都可以转换,或者一个可以转换但转换不明确,则程序格式错误。如果两者都不能转换,则操作数保持不变,并按如下所述执行进一步检查。如果恰好可以进行一次转换,则该转换将应用于所选操作数,并且在本节的其余部分中使用转换后的操作数代替原始操作数。
5.17/4
如果第二个和第三个操作数是左值并且具有相同的类型,则结果是该类型并且是一个左值,如果第二个或第三个操作数是位域,或者两者都是位域,则结果是位域字段。
5.17/5
否则,结果是一个右值。如果第二个和第三个操作数的类型不同,并且都具有(可能是 cv 限定的)类类型,则使用重载决议来确定要应用于操作数(13.3.1.2、13.6)的转换(如果有) . 如果重载决议失败,则程序格式错误。否则,将应用如此确定的转换,并在本节的其余部分使用转换后的操作数代替原始操作数。