auto和decltype的关系
或两者都不是
auto x = initializer;
相当于
decltype(initializer) x = initializer;
或
decltype((initializer)) x = initializer;
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
decltype
还会考虑表达式是rvalue
还是lvalue
。维基百科说,
这几乎解释了重要的区别。注意
decltype(c)
和decltype((c))
不一样!有时
auto
和decltype
以协作方式一起工作,例如下面的示例(取自 wiki,并进行了一些修改):维基百科进一步 解释了
decltype
的语义如下:decltype
also considers whether the expression isrvalue
orlvalue
.Wikipedia says,
That pretty much explains the imporant difference. Notice
decltype(c)
anddecltype((c))
are not same!And sometime
auto
anddecltype
works together in a cooperative way, such as in the following example (taken from wiki, and modified a bit):Wikipedia further explains the semantics of
decltype
as follows:这行不通(而且很难看):
而
会。
This won't work (and is ugly):
whereas
will.
这取决于。
auto
和decltype
具有不同的用途,因此它们不会一对一映射。auto
的规则最容易解释,因为它们与模板参数推导相同。我不会在这里详细介绍它们,但请注意auto&
和auto&&
也是一些可能的用途!然而,decltype 有几种情况,其中一些您在上面已经说明过(信息和引用取自 n3290,7.1.6.2 简单类型说明符 [dcl.type.simple]),我将它们分为两类
非正式地,我想说 decltype 可以对名称(第一种情况)或表达式进行操作。 (正式地,根据语法
decltype
对表达式进行操作,因此将第一种情况视为细化,将第二种情况视为包罗万象。)当使用带有 decltype 的名称时,您将得到 <该实体的 em> 声明类型。例如,decltype(an_object.a_member) 是类定义中出现的成员类型。另一方面,如果我们使用 decltype( (an_object.a_member) ) ,我们会发现自己处于包罗万象的情况,并且我们正在检查表达式的类型,因为它会出现在代码。
因此,如何涵盖您的问题的所有情况:
It depends.
auto
anddecltype
serve different purposes so they don't map one-to-one.The rules for
auto
are the easiest to explain because they are the same as for template parameter deduction. I won't expand on them here, but note thatauto&
andauto&&
are also some possible uses!decltype
however has several cases, some of which you have illustrated above (information and quotes taken from n3290, 7.1.6.2 Simple type specifiers [dcl.type.simple]) that I separate into two categories:Informally, I'd say that
decltype
can operate on either names (for the first case) or expressions. (Formally and according to the grammardecltype
operates on expressions, so think of the first case as a refinement and the second case as a catch-all.)When using a name with decltype, you get the declared type of that entity. So for instance
decltype(an_object.a_member)
is the type of the member as it appears in the class definition. On the other hand, if we usedecltype( (an_object.a_member) )
we find ourselves in the catch-all case and we're inspecting the type of the expression as it would appear in code.Accordingly, how to cover all the cases of your questions:
auto
auto
很简单:它将给出与按值模板参数推导相同的类型。auto
在表达式上统一工作。在 C++ 中,表达式不能具有引用类型(
refint()
的类型为int
而不是int&
)。请注意,表达式的左值性对于右侧表达式(等号右侧或一般被复制的内容)来说不是问题。右值
1
的处理方式与左值i1
和refint()
类似。对于按值参数(即非引用参数),不仅应用左值到右值的转换,还应用数组到指针的转换。 const 被忽略。
decltype
decltype
是一个非常有用的功能,但接口却很糟糕:decltype
对根据名称查找和其他表达式定义的某些表达式的作用不同!这就是让人讨厌 C++ 的特性类型。名为
decltype(entity) 的东西的
decltype
将进行名称查找并给出实体的声明类型。 (entity
可以是非限定或限定标识符,或者是成员访问,例如expr.identifier
。)decltype(f(args))
将进行名称查找和重载解析,并给出函数声明的返回类型,而不是表达式的类型:所以现在我可以使用 decltype 检查我对语言的理解:
sametype::same
存在 iffT
和U
是完全相同的类型。编译良好,所以我没有错!
其他表达式的
decltype
否则,其中
expr
未根据名称查找定义(不是上述情况之一),例如表达式(expr)
、decltype
实现了一个独特的功能(但 C++ 设计者不会在其上花费其他关键字。)decltype(expr)
将给出用它修饰的表达式的类型lxrvalueness (lvalue/xvalue/prvalue-ness):T
类型的纯右值(纯右值)给出T
T
类型的 xvalue 给出T&&
T
类型的左值给出T&
这是函数调用规则的倒数:如果
f
是一个函数带返回类型T&
,表达式f()
是左值T&&
,表达式f()
是xvalueT
,表达式f()
是纯右值,也可用于强制转换:对于裸类型(纯对象类型,非引用)参考)
T
(T&)expr
是左值(T&&)expr
是 xvalue(T)expr
是右值引用性是将 lxrvalueness 编码为类型。 decltype 进行此编码是为了保存和传输事物的 lxrvalueness。
当您想要为表达式添加别名时,这非常有用:作为函数调用的表达式
expr
的 lxrvalueness(普通f(args)
或使用运算符语法,例如>a @ b
) 与声明为decltype(expr) alias();
的alias()
的 lxrvalueness 相同,这可以用于纯转发在通用代码中:
请注意
EXPR_DEC_TYPE(foo())
在设计上等于declexpr(foo())
(在大多数情况下),但计算不同:declexpr(foo (args))
对foo
进行名称查找,并进行重载解析,找到声明,返回准确的返回类型
声明,故事结束
EXPR_DEC_TYPE(foo(args))
然后找到声明的类型计算
表达式的类型
T
,它是裸露的返回类型(不带参考)
根据引用的表达式的 lxrvalueness LXR
声明的返回类型:引用的左值,右引用的x值...
然后用LXR修饰类型
T
以获得decT
类型:如果 LXR = 纯右值,
decT
为T
如果 LXR = xvalue
,
decT
为T&&
如果 LXR = 左值,
decT
为T&
EXPR_DEC_TYPE
返回decT
,与声明的返回类型相同。auto
auto
is simple: it will give the same type as by-value template parameter deduction.auto
works uniformly on expressions.In C++ expressions cannot have reference type (
refint()
has typeint
notint&
).Note that the lvalueness of the expression is not an issue on an expression on the right (on the right of equal sign, or something which is being copied in general). The rvalue
1
is treated like the lvaluesi1
andrefint()
.For by value parameters (that is, non reference parameters), not only lvalue to rvalue conversion is applied, but also array to pointer conversions. const is ignored.
decltype
decltype
is a very useful feature with an horrible interface:decltype
acts differently on some expressions defined in term of name lookup and other expressions! This is the type of features that make people hate C++.decltype
of something nameddecltype(entity)
will do name lookup and give the declared type of the entity. (entity
can be an unqualified or qualified identifier, or a member access such asexpr.identifier
.)decltype(f(args))
will do name lookup and overload resolution and give the declared return type of the function, not the type the expression:So now I can check my understanding of the language with
decltype
:sametype<T,U>::same
exists iffT
andU
are exactly the same type.compiles fine, so I was not wrong!
decltype
of other expressionsOtherwise, where
expr
is not defined in term of name lookup (not one of the above cases), such as the expression(expr)
,decltype
implements a distinct feature (but the C++ designers would not spent another keywords on it.)decltype(expr)
will give the type of the expression decorated with its lxrvalueness (lvalue/xvalue/prvalue-ness):T
givesT
T
givesT&&
T
givesT&
This is the reciprocal of function call rule: if
f
is a function with return typeT&
, the expressionf()
is an lvalueT&&
, the expressionf()
is an xvalueT
, the expressionf()
is a prvalueand also for casts: for naked type (pure object type, non reference)
T
(T&)expr
is a lvalue(T&&)expr
is an xvalue(T)expr
is a prvalueReference-ness is the encoding of lxrvalueness into types.
decltype
does this encoding to save and transfer the lxrvalueness of things.This is useful when you want to alias an expression: the lxrvalueness of an expression
expr
that is a function call (either normalf(args)
or with operator syntax likea @ b
) is the same as the lxrvalueness ofalias()
declared asdecltype(expr) alias();
This can be used for pure forwarding in generic code:
Note that
EXPR_DEC_TYPE(foo())
equalsdeclexpr(foo())
by design (in most cases), but the computations are different:declexpr(foo(args))
does name lookup forfoo
, does overloadresolution, finds the declaration, returns the exact return type
declared, end of story
EXPR_DEC_TYPE(foo(args))
finds the type of the declaration thencomputes
the type
T
of the expression which is the return type naked (withoutreference)
the lxrvalueness LXR of the expression according to the referenceness
of the declared return type: lvalue for reference, xvalue of r-reference...
then it decorates the type
T
with LXR to obtain thedecT
type:decT
isT
if LXR = prvaluedecT
isT&&
if LXR = xvaluedecT
isT&
if LXR = lvalueEXPR_DEC_TYPE
returnsdecT
which is the same as the declared return type.initializer
是一个数组,则decltype(x)
或decltype((x))
不要简单地致力于它。然而
auto
将被推导为指针。
initializer
是一个函数,那么应用decltype(fp)
将然而,推导出函数类型,
auto
将推导出其返回类型。
因此,一般来说
auto
不能被视为替代您要求的任何decltype()
版本。initializer
is an array thendecltype(x)
ordecltype((x))
don't work simply on it. However
auto
will be deduced to apointer.
initializer
is a function then applyingdecltype(fp)
willdeduce to the function type however,
auto
will deduce to itsreturn type.
So in general
auto
cannot be considered as a replacement of anydecltype()
version you asked.