表达式的类型没有任何引用痕迹。因此,如果我们暂时假设引用可以具有引用类型,那么我们将得到以下结果:
int a = 0;
int &ra = a;
int c = a + 42;
int d = ra + 42;
在上述代码中,表达式
a
将会 有类型
int
,而表达式
ra
将会 有类型
int&
。在规范的几乎所有关于表达式类型的规则中,例如规则说“表达式 E 必须是类型 X” ,我们都需要添加"...或者引用类型 X"(考虑强制类型转换运算符)。因此,我猜这会是太过繁琐,不便于使用。
C++有以下类型
如果没有明确说明指的是动态类型,则称为“表达式的类型”。这是一个表达式的属性,在编译时,抽象掉了表达式所指的内容。例如,如果a
引用了一个int&
或 int
变量,或者是字面值0
,那么所有这些表达式都有类型int
。
这是左值表达式所引用的非基类对象的类型。
ofstream fs("/log");
ostream &os = fs;
在这里,os
的静态类型是ostream
,而动态类型是ofstream
。
这是一个对象或引用实际具有的类型。一个对象总是有单一类型,并且它的类型永远不会改变。但是,什么对象存在于哪个位置只有在运行时才知道,因此通常,“对象的类型”也是一个运行时的事情。
ostream *os;
if(file)
os = new ofstream("/log");
else
os = new ostringstream;
由*os
表示的对象类型(以及lvalue *os
的动态类型)仅在运行时知道。
int *p = new int[rand() % 5 + 1];
在这里,通过operator new创建的数组类型只在运行时才知道,并且(幸运的是)不会逃逸到静态C++类型系统中。臭名昭着的别名规则(大致上禁止从不兼容的lvalue读取对象)涉及到对象的"动态类型",可能是因为它想强调运行时关注点的重要性。但严格来说,说一个对象的"动态类型"很奇怪,因为一个对象没有"静态类型"。
这是你在声明中给出的类型。与对象类型或表达式类型相比,这有时可能略有不同。
struct A {
A() { }
int a;
};
const A *a = new const A;
volatile const A *va = a;
这里,表达式
a->a
的类型为
const int
,但是
a->a
解析到的成员实体声明的类型为
int
。因为我们用
new
表达式创建了一个
const A
对象,所以所有非静态数据成员都是隐式地
const
子对象,对象的类型为
const int
。 在
va->a
中,该表达式的类型为
volatile const int a
,变量的声明类型仍然为
int
,引用的对象类型仍然为
const int
。
当你说“a的类型”,并且将“a”声明为 “int &a”时,你必须总是说明你所说的“a”的类型是什么。你是指表达式吗?那么“a”的类型为
int
。甚至会更糟。想象一下“int a[10]”;这里,“a”的表达式类型为
int*
或
int[10]
,这取决于你是否认为在你的表达式中进行了数组到指针转换,当你询问“a”的类型时。如果你询问“a”所引用的变量的类型,那么答案唯一地是
int
和
int[10]
。
那么rvalue可能具有什么类型呢?Rvalues是表达式。
int &&x = 0;
int y = std::move(x);
int z = x;
这里有两个 rvalue,一个是
0
,另一个是
std::move(x)
,它们都是类型为
int
的 rvalue。对于变量
z
的初始化,出现在赋值号左侧的
x
是一个 lvalue,尽管它引用了与 rvalue
std::move(x)
相同的对象。
你问题中关于重载函数如何处理 rvalue 和 lvalue 的最后一点很有趣。虽然 rvalue 引用写作
int &&
,但这并不意味着 rvalues 的类型是
int
。它们被称为 rvalue 引用是因为你可以使用 rvalues 来初始化它们,并且语言更喜欢使用 rvalue 初始化 rvalue 引用而不是 lvalue 引用。
此外,看到表达式中以名称形式出现的 rvalues 可能很有用。
enum A { X };
template<int Y> struct B { };
如果您使用
X
或者
Y
,它们是右值。但这些似乎是我能想到的唯一情况。
rRef
是类型MyType
。R/Lvalueness是表达式的属性,而不是类型的属性。 - dypfunc()
内部调用行的括号,而不是gunc()
的参数声明。 - Kristian D'Amatostd::move(rRef)
和rRef
(作为表达式)的类型都只是int
。左/右值用于选择重载(在这种情况下:左值无法绑定到MyType&&
,右值无法绑定到MyType&
,因此每种情况只有一个可行的重载)。 - dyp