对哪些表达式应用decltype后产生引用类型?< / h1 >



我正在阅读c++入门,不太明白表达式何时产生对象类型,何时产生对象的引用类型。

我从书中引用:

  1. 当我们对一个不是变量的表达式应用decltype时,我们得到该表达式产生的类型。
  2. 一般来说,decltype为产生的表达式返回一个引用类型

考虑下面的代码:

int i = 3, *ptr = &i, &ref = i;
decltype(ref + 0) j;

在上面的代码中,表达式"ref + 0"导致了一个固有的操作,即对ref引用的对象i和0的值进行相加。因此,根据第一条规则,表达式产生int类型。但是根据第二条规则,由于表达式产生的对象类型可以位于赋值操作的左侧(在本例中为int),那么decltype不应该产生ref to int(int&)类型吗?

书上还说,对于下面的代码

decltype(*ptr) k;

k的类型为int&而不是int,即表达式生成的类型。

它还表示,对于像

下面代码中的赋值表达式
decltype(a = b) l;

l应该在赋值操作的左侧有object的引用类型。

我们如何知道哪些表达式产生对象类型,哪些表达式产生对对象类型的引用?

不正式地理解这些概念是不容易的。入门可能不想让您感到困惑,并避免引入诸如">左值"、">右值"one_answers">xvalue"之类的术语。不幸的是,这些是理解decltype如何工作的基础。

首先,求值表达式的类型既不是引用类型,也不是非类类型(例如int constint&)的顶级const限定类型。如果表达式的类型为int&int const,则在进一步求值之前立即将其转换为int

这在c++ 11标准的第5/5和5/6段中有规定:

5如果一个表达式最初的类型是" reference to T "(8.3.2, 8.5.3),则该类型先调整为T有进一步的分析吗?表达式指定由引用表示的对象或函数,而表达式是左值xvalue,具体取决于表达式。

6如果prvalue的初始类型为" cv T ",其中T是cv不限定的非类、非数组类型,则类型为在进一步分析之前,表达式被调整为T

表达式到此为止。decltype做什么?对于给定表达式e,确定decltype(e)结果的规则在第7.1.6.2/4段中指定:

decltype(e)表示的类型定义如下:

—如果e是一个未加括号的id-expression或一个未加括号的类成员access (5.2.5),decltype(e). 0为e命名的实体类型。如果没有这样的实体,或者e命名了一组重载函数,程序格式错误;

—否则,如果exvalue,则decltype(e)T&&,其中Te的类型;

—否则,如果e左值,则decltype(e)T&,其中Te的类型;

—否则,decltype(e)e的类型。

decltype说明符的操作数是一个未求值的操作数(第5条)。

这听起来确实令人困惑。让我们试着一步一步地分析。首先:

-如果e是未加括号的id-expression或未加括号的类成员访问(5.2.5),则decltype(e). s

-为e命名的实体类型。如果没有这样的实体,或者e命名了一组重载函数,程序格式错误;

这很简单。如果e只是变量的名称,并且没有将其放在括号内,那么decltype的结果就是该变量的类型。所以

bool b; // decltype(b) = bool
int x; // decltype(x) = int
int& y = x; // decltype(y) = int&
int const& z = y; // decltype(z) = int const&
int const t = 42; // decltype(t) = int const

注意,这里decltype(e)的结果不一定与求值表达式e的类型相同。例如,表达式z的求值产生int const类型的值,而不是int const&类型的值(因为在第5/5段中,&被剥离了,正如我们前面看到的)。

让我们看看当表达式不只是一个标识符时会发生什么:

—否则,如果exvalue,则decltype(e)T&&,其中Te的类型;

事情越来越复杂了。什么是xvalue?基本上,它是表达式可以属于的三个类别之一(xvaluelvalueprvalue)。xvalue通常在调用返回类型为右值引用类型的函数时获得,或者作为静态强制转换为右值引用类型的结果。典型的例子是调用std::move()

使用标准中的措辞:

[注意:如果表达式为

,则为xvalue

——隐式或显式调用返回类型为右值引用的函数的结果对象类型

-转换为对象类型的右值引用,

——类成员访问表达式,指定非引用类型的非静态数据成员,其中对象表达式为xvalue

.*的成员指针表达式,其中第一个操作数是xvalue,第二个操作数是指向数据成员的指针。

一般来说,该规则的效果是命名右值引用被视为左值,未命名右值对对象的引用被视为xvalues;右值对函数的引用被视为左值不管有没有名字。-end note]
例如,表达式std::move(x)static_cast<int&&>(x)std::move(p).first(对于类型为pair的对象p)是xvalue。当您将decltype应用于xvalue表达式时,decltype&&附加到表达式的类型后面:
int x; // decltype(std::move(x)) = int&&
// decltype(static_cast<int&&>(x)) = int&&

让我们继续:

—否则,如果e左值,则decltype(e)T&,其中Te的类型;

左值是什么?非正式地说,lvalue

表达式是表示可以在程序中重复引用的对象的表达式,例如具有名称的变量和/或可以取其地址的对象。对于类型为T左值表达式e,decltype(e)的结果为T&。例如:

int x; // decltype(x) = int (as we have seen)
// decltype((x)) = int& - here the expression is parenthesized, so the
// first bullet does not apply and decltype appends & to the type of
// the expression (x), which is int

对于返回类型为T&的函数的函数调用也是左值表达式,因此:

int& foo() { return x; } //  decltype(foo()) = int& 
最后

:

—否则,decltype(e)e的类型。

如果表达式既不是xvalue也不是lvalue(换句话说,如果它是prvalue),则decltype(e)的结果就是e的类型。未命名的临时值和字面量是右值。例如:

int foo() { return x; } // Function calls for functions that do not return
// a reference type are prvalue expressions
// decltype(foo()) = int
// decltype(42) = int

让我们把上面的应用到你的问题中的例子。给定这些声明:

int i = 3, *ptr = &i, &ref = i;
decltype(ref + 0) j;
decltype(*ptr) k;
decltype(a = b) l;

j的类型将是int,因为operator +返回类型为intprvaluek的类型将是int&,因为一元operator *产生一个左值(见第5.3.1/1段)。l的类型也是int&,因为operator =的结果是左值(见第5.17/1段)。

关于你问题的这一部分:

但是根据第二条规则,由于表达式产生的对象类型可以位于赋值操作的左侧(在本例中为int),那么decltype不应该产生ref to int(int&)类型吗?

你可能误解了书中的那一段。并非所有int类型的对象都可以位于赋值操作的左侧。例如,下面的赋值是非法的:
int foo() { return 42; }
foo() = 24; // ERROR! foo() is a prvalue expression, cannot be on the left
// side of an assignment

表达式是否可以出现在赋值操作的左侧(注意,这里我们讨论的是基本数据类型的内置赋值操作符)取决于该表达式的值类别(左值x值prvalue),表达式的值类别与其类型无关。

对于表达式,如示例中所示,如果参数为左值,decltype将提供一个引用类型。

7.1.6.2p4:

The type denoted by decltype(e) is defined as follows:
— if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e)     is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions,     the program is ill-formed;
— otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;
— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
— otherwise, decltype(e) is the type of e.
The operand of the decltype specifier is an unevaluated operand (Clause 5).
[ Example:
const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = i; // type is const int&&
decltype(i) x2; // type is int
decltype(a->x) x3; // type is double
decltype((a->x)) x4 = x3; // type is const double&
—end example ]

最新更新