将decltype应用于表达式时,哪些表达式会产生引用类型? [英] What expressions yield a reference type when decltype is applied to them?

查看:62
本文介绍了将decltype应用于表达式时,哪些表达式会产生引用类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读C ++ Primer,却不太理解表达式何时产生对象类型,以及何时产生对象的引用类型。

I was reading C++ Primer and couldn't quite understand when an expression yields an object type, and when it yields a reference type to the object.

从书中:



  1. 当我们将decltype应用于不是变量的表达式时,我们得到了类型

  2. 通常来说,decltype返回表达式的引用类型,该表达式产生的
    对象可以位于赋值的左侧。
  3. >
  1. When we apply decltype to an expression that is not a variable, we get the type that > that expression yields.
  2. 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和i。因此,按照第一个规则,该表达式产生一个int类型。
但是,按照第二条规则,因为表达式产生的对象的类型可以站在赋值的左侧(在本例中为int),因此decltype不应为int(int& ;)类型?

In the above code, the expression "ref + 0" results in an inherent operation of addition of value of the object that ref refers to, i and 0. Hence, going by the first rule the expression yields an int type. 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?

该书还说,对于以下代码

The book also says, for the following code

decltype(*ptr) k;

k类型为int&

k has type int& and not int, the type which the expression results in.

它也表示对于赋值表达式,如下面的代码所示

It also says that for an assignment expression like in code below

decltype(a = b) l;

l在分配操作的左侧具有对对象的引用类型。

l would have the type of reference to object on the left hand side of the assignment operation.

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

How would we know which expressions yield the object type and which yield the reference to the object type?

推荐答案

不正式就很难理解这些概念。入门可能不想让您感到困惑,并避免引入诸如 lvalue rvalue xvalue 之类的术语。不幸的是,这些是了解 decltype 的工作原理的基础。

It is not easy to understand these concepts without getting formal. The primer probably does not want to confuse you and avoids introducing terms such as "lvalue", "rvalue", and "xvalue". Unfortunately, these are fundamental in order to understand how decltype works.

首先,被评估对象的类型表达式既不是引用类型,也不是非类类型的顶级 const 限定类型(例如, int const int& )。如果表达式的类型原来是 int& int const ,则它会立即转换为 int 之前的任何进一步评估。

First of all, the type of an evaluated expression is never a reference type, nor a top-level const-qualified type for non-class types (e.g. int const or int&). If the type of an expression turns out to be int& or int const, it gets immediately transformed into int prior to any further evaluation.

这在C ++ 11标准的第5/5和5/6段中指定:

This is specified in paragraphs 5/5 and 5/6 of the C++11 Standard:


5如果表达式最初的类型为对T的引用(8.3.2,8.5.3),则该类型会先调整为 T
进行任何进一步的分析。表达式指定由引用表示的对象或函数,取决于表达式,
表达式是 lvalue xvalue

6如果 prvalue 最初的类型为 cv T,则 T 是cv不合格的非类,非数组类型,在进一步分析之前将表达式的
类型调整为 T

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 to T prior to any further analysis.

这么多的表达式。 decltype 有什么作用?好吧,确定给定表达式 e decltype(e)结果的规则在第7.1节中指定。 6.2 / 4:

So much for expressions. What does decltype do? Well, the rules that determine the result of decltype(e) for a given expression e are specified in paragraph 7.1.6.2/4:


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

The type denoted by decltype(e) is defined as follows:

-如果 e 是未括号的 id-expression 或未括号的类成员访问权限(5.2.5), decltype(e)
是由 e 。如果没有这样的实体,或者如果 e 命名一组重载函数,则
程序格式错误;

— 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;

-否则,如果 e xvalue ,则 decltype(e) T& ,其中 T e ;

— otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;

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

— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;

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

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

The operand of the decltype specifier is an unevaluated operand (Clause 5).

这的确令人困惑。让我们尝试进行部分分析。首先:

This can indeed sound confusing. Let's try to analyze it part by part. First of all:


-如果 e 是未括号的 id-表达式或未括号的类成员访问权限(5.2.5), decltype(e)
是由<$ c $命名的实体的类型c> e
。如果没有这样的实体,或者如果 e 命名一组重载函数,则
程序格式不正确;

— 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;

这很简单。如果 e 只是变量的名称,而没有将其放在括号中,则 decltype 的结果为该变量的类型。所以

This is simple. If e is just the name of a variable and you do not put it within parentheses, then the result of decltype is the type of that variable. So

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段删​​除了& )。

Notice, that the result of decltype(e) here is not necessarily the same as the type of the evaluated expression e. For instance, the evaluation of the expression z yields a value of type int const, not int const& (because by paragraph 5/5 the & gets stripped away, as we have seen previously).

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

Let's see what happens when the expression is not just an identifier:


-否则,如果 e xvalue decltype(e) T&& ; ,其中 T e 的类型;

— otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;

这变得越来越复杂。什么是 xvalue ?基本上,它是表达式可以属于的三个类别之一( xvalue lvalue prvalue )。通常,调用返回类型为 rvalue 引用类型的函数或静态转换为 rvalue 的结果时,通常会获得 xvalue 。 em>引用类型。典型示例是对 std :: move()的调用。

This is getting complicated. What is an xvalue? Basically, it is one of the three categories an expression can belong to (xvalue, lvalue, or prvalue). An xvalue is normally obtained when invoking a function with a return type which is an rvalue reference type, or as the result of a static cast to an rvalue reference type. The typical example is a call to std::move().

要使用标准中的措辞:

To use the wording from the Standard:


[注意:如果表达式为 xvalue ,则为:

-调用函数的结果,无论隐式还是显式,其返回类型为 rvalue 引用
到对象类型

— the result of calling a function, whether implicitly or explicitly, whose return type is an rvalue reference to object type,

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

— a cast to an rvalue reference to object type,

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

— 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 。* 指向成员的表达式,其中第一个操作数是 xvalue ,第二个操作数是
a指向数据成员的指针。

— a .* pointer-to-member expression in which the first operand is an xvalue and the second operand is a pointer to data member.

通常,此规则的作用是将已命名的 rvalue 引用视为 lvalues 和未命名的 rvalue
对对象ar的引用e视为 xvalues ;不管
是否命名,对函数的 rvalue 引用均视为 lvalues 。 —尾注]

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 (对于对象 pair 类型的对象 p )是x值。当将 decltype 应用于 xvalue 表达式时, decltype 会附加 && 到表达式的类型:

So for instance, the expressions std::move(x), static_cast<int&&>(x), and std::move(p).first (for an object p of type pair) are xvalues. When you apply decltype to an xvalue expression, decltype appends && to the type of the expression:

int x; // decltype(std::move(x)) = int&&
       // decltype(static_cast<int&&>(x)) = int&&

让我们继续:


-否则,如果 e lvalue ,则 decltype(e) T& ,其中 T e 的类型;

— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;

什么是左值?好吧,非正式地, lvalue 表达式是表示可以在程序中重复引用的对象的表达式-例如具有名称的变量和/或可以作为其地址的对象。

What is an lvalue? Well, informally, lvalue expression are expressions which denote objects that can be repeatably referenced in your program - for instance variables with a name and/or objects you can take the address of.

对于 T 类型的表达式 e 左值表达式, decltype(e)产生 T& 。例如:

For an expression e of type T that is an lvalue expression, decltype(e) yields T&. So for instance:

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& 的函数也是 lvalue 表达式,因此:

A function call for a function whose return type is T& is also an lvalue expression, so:

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

最后:


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

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

If the expression is not an xvalue nor an lvalue (in other words, if it is a prvalue), the result of decltype(e) is simply the type of e. Unnamed temporaries and literals are prvalues. So for instance:

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

// decltype(foo()) = int
// decltype(42) = int






让以上内容适用于示例从你的问题。给定以下声明:


Let's apply the above to the examples from your question. Given these declarations:

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

j 的类型为 int ,因为 operator + 返回类型为 int prvalue code>。 k 的类型将为 int& ,因为一元运算符* 产生一个 lvalue (请参见第5.3.1 / 1段)。 l 的类型也是 int& ,因为 operator = 左值(请参见第5.17 / 1段)。

The type of j will be int, because operator + returns a prvalue of type int. The type of k will be int&, because the unary operator * yields an lvalue (see paragraph 5.3.1/1). The type of l is also int&, because the result of operator = is an lvalue (see paragraph 5.17/1).

关于问题的这一部分:


但按照第二条规则,因为表达式产生的对象的类型可以站在赋值的左侧(在这种情况下为int), decltype是否应该产生对int(int&)类型的引用?

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?

您可能会误解本书中的这一段。不是 all 类型的 int 对象可以位于分配的左侧。例如,以下分配是非法的:

You probably misinterpreted that passage from the book. Not all objects of type int can be on the left side of an assignment. For instance, the assignment below is illegal:

int foo() { return 42; }

foo() = 24; // ERROR! foo() is a prvalue expression, cannot be on the left
            // side of an assignment

表达式是否可以出现在赋值的左侧(请注意,此处我们正在讨论基本数据类型的内置赋值运算符)取决于值表达式的类别 lvalue xvalue prvalue ),表达式的值类别与其类型无关

Whether or not an expression can appear on the left side of an assignment (notice, that we are talking about the built-in assignment operator for fundamental data types here) depends on the value category of that expression (lvalue, xvalue, or prvalue), and the value category of an expression is independent from its type.

这篇关于将decltype应用于表达式时,哪些表达式会产生引用类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆