我正在阅读C Primer,当表达式产生一个对象类型,并且它产生一个对象的引用类型时,我不太明白.
我从这本书中引用:
- When we apply decltype to an expression that is not a variable, we get the type that > that expression yields.
- Generally speaking, decltype returns a reference type for expressions that yield
objects that can stand on the left-hand side of the assignment.
考虑以下代码:
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将在赋值操作的左侧具有对象的引用类型.
我们如何知道哪些表达式产生对象类型以及哪些表达式产生对象类型的引用?
解决方法:
如果不正式就理解这些概念并不容易.引子可能不想让你迷惑并避免引入诸如“左值”,“右值”和“x值”之类的术语.不幸的是,这些是了解decltype如何工作的基础.
首先,求值表达式的类型永远不是引用类型,也不是非类类型的*const限定类型(例如int const或int&).如果表达式的类型变成int&或者int const,它会在进一步评估之前立即转换为int.
这在C 11标准的第5/5和第5/6段中有详细说明:
5 If an expression initially has the type “reference to T” (8.3.2, 8.5.3), the type is adjusted to
T
prior to
any further analysis. The expression designates the object or function denoted by the reference, and the
expression is an lvalue or an xvalue, depending on the expression.6 If a prvalue initially has the type “cv T,” where
T
is a cv-unqualified non-class, non-array type, the type of
the expression is adjusted toT
prior to any further analysis.
表达方式如此之多. decltype做什么?那么,确定给定表达式e的decltype(e)结果的规则在7.1.6.2/4中规定:
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 bye
. If there is no such entity, or ife
names a set of overloaded functions,
the program is ill-formed;— otherwise, if
e
is an xvalue,decltype(e)
isT&&
, whereT
is the type ofe
;— otherwise, if
e
is an lvalue,decltype(e)
isT&
, whereT
is the type ofe
;— otherwise,
decltype(e)
is the type ofe
.The operand of the
decltype
specifier is an unevaluated operand (Clause 5).
这听起来确实令人困惑.让我们尝试逐个分析它.首先:
— 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 bye
. If there is no such entity, or ife
names a set of overloaded functions,
the program is ill-formed;
这很简单.如果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类型的值,而不是int const& (因为根据第5/5段,&被剥夺了,正如我们之前所见).
让我们看看当表达式不仅仅是一个标识符时会发生什么:
— otherwise, if
e
is an xvalue,decltype(e)
isT&&
, whereT
is the type ofe
;
这变得复杂了.什么是xvalue?基本上,它是表达式可以属于的三个类别之一(xvalue,lvalue或prvalue). xvalue通常在调用具有rvalue引用类型的返回类型的函数时获得,或者作为静态强制转换为rvalue引用类型的结果获得.典型的例子是对std :: move()的调用.
要使用标准中的措辞:
[ Note: An expression is an xvalue if it is:
— the result of calling a function, whether implicitly or explicitly, whose return type is an rvalue reference
to object type,— a cast to an rvalue reference to object type,
— a class member access expression designating a non-static data member of non-reference type in which
the object expression is an xvalue, or— a
.*
pointer-to-member expression in which the first operand is an xvalue and the second operand is
a pointer to data member.In general, the effect of this rule is that named rvalue references are treated as lvalues and unnamed rvalue
references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether
named or not. —end note ]
因此,例如,表达式std :: move(x),static_cast< int&&>(x)和std :: move(p).first(对于类型对的对象p)是xvalues.将decltype应用于xvalue表达式时,decltype会附加&&到表达式的类型:
int x; // decltype(std::move(x)) = int&&
// decltype(static_cast<int&&>(x)) = int&&
让我们继续:
— otherwise, if
e
is an lvalue,decltype(e)
isT&
, whereT
is the type ofe
;
什么是左值?嗯,非正式地,左值表达式是表示可以在程序中重复引用的对象的表达式 – 例如具有名称的变量和/或可以取地址的对象.
对于作为左值表达式的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&
最后:
— otherwise,
decltype(e)
is the type ofe
.
如果表达式不是xvalue或lvalue(换句话说,如果它是prvalue),则decltype(e)的结果就是e的类型.未命名的临时文字和文字都是prvalues.例如:
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返回int类型的prvalue. k的类型将是int&,因为一元运算符*产生左值(见第5.3.1 / 1段). l的类型也是int&,因为operator =的结果是左值(见第5.17 / 1段).
关于这部分问题:
But going by the second rule, as the expression yields the type of an object that can stand on the left hand side of an assignment (in this case int), shouldn’t the decltype yield a ref to int(int&) type?
你可能误解了书中的这段经文.并非所有int类型的对象都可以位于赋值的左侧.例如,以下分配是非法的:
int foo() { return 42; }
foo() = 24; // ERROR! foo() is a prvalue expression, cannot be on the left
// side of an assignment
表达式是否可以出现在赋值的左侧(注意,我们在这里讨论基本数据类型的内置赋值运算符)取决于该表达式的值类别(lvalue,xvalue或prvalue) ,表达式的值类别与其类型无关.