我正在阅读C ++ Primer,当表达式产生一个对象类型,并且它产生一个对象的引用类型时,我不太明白。
我引用了这本书:
- 当我们将decltype应用于非变量的表达式时,我们得到的类型为>那个表达产生了。
- 一般来说,decltype返回yield的表达式的引用类型 可以站在作业左侧的对象。
醇>
考虑以下代码:
int i = 3, *ptr = &i, &ref = i;
decltype(ref + 0) j;
在上面的代码中,表达式“ref + 0”导致添加ref引用的对象的值的固有操作,i和0.因此,通过第一个规则,表达式产生int类型。 但是按照第二条规则,因为表达式产生的对象类型可以位于赋值的左侧(在本例中为int),decltype不应该产生对int(int&)类型的引用吗?
该书还说,对于以下代码
decltype(*ptr) k;
k的类型为int&而不是int,表达式导致的类型。
它还表示对于分配表达式,如下面的代码
decltype(a = b) l;
l将在赋值操作的左侧具有对象的引用类型。
我们如何知道哪些表达式产生对象类型以及哪些表达式产生对象类型的引用?
答案 0 :(得分:40)
如果不正式,理解这些概念并不容易。引子可能不想让你感到困惑,并且避免引入诸如" 左值"," rvalue "等术语,以及" x值&#34 ;.不幸的是,这些是了解decltype
如何运作的基础。
首先,评估表达式的类型绝不是引用类型,也不是非类类型的顶级const
- 限定类型(例如int const
或int&
)。如果表达式的类型为int&
或int const
,则会在进行任何进一步评估之前立即转换为int
。
这在C ++ 11标准的第5/5和5/6段中有详细说明:
5如果表达式最初具有“对T的引用”类型(8.3.2,8.5.3),则在将类型调整为
T
之前 进一步的分析。表达式指定由引用表示的对象或函数,以及 表达式是左值或 xvalue ,具体取决于表达式。6如果 prvalue 最初的类型为“cv T”,其中
T
是一个cv非合格的非类,非数组类型, 在进行任何进一步分析之前,将表达式调整为T
。
表达方式太多了。 decltype
做了什么?那么,确定给定表达式decltype(e)
的{{1}}结果的规则在7.1.6.2/4段中规定:
e
表示的类型定义如下:- 如果
decltype(e)
是未加密码的 id-expression 或未加括号的类成员访问权限(5.2.5),e
是decltype(e)
命名的实体的类型。如果没有这样的实体,或者e
命名一组重载函数, 该计划格式不正确;- 否则,如果
e
是 xvalue ,则e
为decltype(e)
,其中T&&
是T
的类型;- 否则,如果
e
是左值,则e
为decltype(e)
,其中T&
是T
的类型;- 否则,
e
是decltype(e)
的类型。
e
说明符的操作数是未评估的操作数(第5条)。
这确实听起来令人困惑。让我们尝试逐个分析它。首先:
- 如果
decltype
是未加密码的 id-expression 或未加括号的类成员访问权限(5.2.5),e
是decltype(e)
命名的实体的类型。如果没有这样的实体,或者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
(因为第5/5段int const&
被剥离了,因为我们有之前见过。)
让我们看看当表达式不仅仅是一个标识符时会发生什么:
- 否则,如果
&
是 xvalue ,则e
为decltype(e)
,其中T&&
是T
的类型;
这变得复杂了。什么是 xvalue ?基本上,它是表达式可以属于的三个类别之一( xvalue , lvalue 或 prvalue )。通常在调用返回类型为 rvalue 引用类型的函数时,或者作为静态强制转换为 rvalue的结果时,会获得 xvalue em>参考类型。典型的例子是对e
的调用。
使用标准中的措辞:
[注意:表达式是 xvalue ,如果它是:
- 调用函数的结果,无论是隐式还是显式,其返回类型是 rvalue 引用 对象类型,
- 对 rvalue 对象类型的引用
的强制转换- 一个类成员访问表达式,指定非引用类型的非静态数据成员 对象表达式是 xvalue 或
-
std::move()
指向成员的表达式,其中第一个操作数是 xvalue ,第二个操作数是 指向数据成员的指针。通常,此规则的作用是将名为 rvalue 的引用视为 lvalues 和未命名的 rvalue 对象的引用被视为 xvalues ; rvalue 对函数的引用被视为 lvalues 是否 是否命名。 - 后注]
因此,例如,表达式.*
,std::move(x)
和static_cast<int&&>(x)
(对于std::move(p).first
类型的对象p
)是xvalues。当您将pair
应用于 xvalue 表达式时,decltype
会将decltype
附加到表达式的类型中:
&&
让我们继续:
- 否则,如果
int x; // decltype(std::move(x)) = int&& // decltype(static_cast<int&&>(x)) = int&&
是左值,则e
为decltype(e)
,其中T&
是T
的类型;
什么是左值?好吧,非正式地, lvalue 表达式是表示可以在程序中重复引用的对象的表达式 - 例如带有名称的变量和/或可以取地址的对象。
对于e
类型的表达式e
,它是左值表达式,T
会产生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)
的类型。
如果表达式不是 xvalue ,也不是左值(换句话说,如果它是 prvalue ),则结果为{ {1}}只是e
的类型。未命名的临时文字和文字是 prvalues 。例如:
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
类型的 prvalue 。 operator +
的类型将为int
,因为一元k
会产生左值(参见第5.3.1 / 1段)。 int&
的类型也是operator *
,因为l
的结果是左值(参见第5.17 / 1段)。
关于你问题的这一部分:
但是按照第二条规则,因为表达式产生的对象类型可以位于赋值的左侧(在本例中为int),所以decltype不应该将ref引入int( int&amp;)类型?
你可能误解了这本书的内容。不是类型int&
的所有对象都可以位于作业的左侧。例如,下面的分配是非法的:
operator =
表达式是否可以出现在赋值的左侧(注意,我们在这里讨论基本数据类型的内置赋值运算符)取决于该表达式的值类别(左值, xvalue 或 prvalue ),表达式的值类别独立于其类型。
答案 1 :(得分:3)
对于表达式,如示例所示,如果参数为左值,则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 ]