在C ++中,当decltype应用于它们时,什么表达式产生引用类型? [英] In C++, what expressions yield a reference type when decltype is applied to them?

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

问题描述

我是C ++的初学者。我正在阅读C ++ Primer,当一个表达式产生一个对象类型时,以及当它产生一个对象的引用类型时,我不太明白。



我从书中引用:



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


  2. 一般来说,decltype返回一个引用类型,用于生成可以位于作业左侧的
    对象。 $ b


考虑以下代码:

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

在上面的代码中,表达式ref + 0的参考引用的对象,i和0.因此,按照第一个规则,表达式产生一个int类型。
但是按照第二条规则,因为表达式产生可以位于赋值(在这种情况下为int)左侧的对象的类型,所以不应该使用decltype产生一个ref to int(int& amp ;)type?



本书还说,对于以下代码

  decltype * ptr)k; 

k类型为int&



它还说,对于一个赋值表达式,如下面的代码

  decltype(a = b)l; 

l将在赋值操作的左侧具有对对象的引用类型。 p>

我们如何知道哪些表达式产生对象类型,哪些产生对对象类型的引用?任何帮助是赞赏。提前感谢。

解决方案

在不正式的情况下理解这些概念并不容易。引号可能不想混淆你,避免引入诸如 lvalue rvalue xvalue 这样的术语。不幸的是,这些是基本的,以了解 decltype 如何工作。



首先,表达式从不是引用类型,也不是非类型类型的顶层 const 类型(例如 int const int& )。如果表达式的类型为 int& int const ,它立即转换为 int



这在C ++ 11标准的第5/5和第5/6节中有详细说明:


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



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


表达式太多了。 decltype 是什么?那么,在第7.1节中规定了给定表达式 e decltype(e)结果的规则。 6.2 / 4:


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



- 如果 e 是未加掩饰的 id-表达式成员访问(5.2.5), decltype(e)
是由 e命名的实体的类型。如果没有这样的实体,或者 e 命名一组重载函数,
程序是不成形的;



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



- 否则,如果 e code> decltype(e)是 T& ,其中 T 类型 e ;



- 否则 decltype(e) 的类型



decltype的操作数说明符是未经评估的操作数(第5条)。


这听起来很混乱。让我们尝试一部分分析它。首先:


- 如果 e 是未加括号的<表达式或未加括号的类成员访问(5.2.5), 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 的值,而不是 int const& (因为在第5/5段中,& 被删除,就像我们之前看到的)。



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


c $ c> e 是 xvalue decltype(e) T& ; ,其中 T e 的类型;


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




[注意:如果是:


$表达式是一个 xvalue b $ b

- 隐式或显式调用函数的结果,返回类型为 rvalue 引用
到对象类型,



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



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



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



一般来说,此规则的效果是,名为 rvalue 的引用被视为 lvalues 和未命名的 rvalue
对对象的引用被视为 xvalues ;对引用的函数将被视为 lvalue ,无论
是否命名。 -end note]


因此,例如,表达式 std :: move(x) static_cast< int&&>(x) std :: move(p).first (对于对象 p )是x值。将 decltype 应用于 xvalue 表达式时, decltype &&&& 到表达式的类型:

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

让我们继续:


- 否则如果 e lvalue decltype(e) 的类型 ;


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



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

  int x; // decltype(x)= int(正如我们所看到的)
// decltype((x))= int& - 这里的表达式是括号括起来的,所以
//第一个项目符号不应用和decltype追加&到类型
//表达式(x),它是int

调用返回类型为 T& 的函数也是一个表达式,因此:

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

最后:


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


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

  int foo(){return x; } //函数调用不返回
//引用类型是prvalue表达式

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






让我们将上面的例子从你的问题。给定这些声明:

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

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

关于您的问题的这一部分:


但是按照第二条规则,因为表达式产生一个可以位于赋值(在这种情况下为int)左侧的对象的类型,应该不应该decltype yield a ref to int(int&)type?


你可能误解了这本书的段落。不是 int 类型的全部对象可以在作业的左侧。例如,下面的赋值是非法的:

  int foo(){return 42; } 

foo()= 24; //错误! foo()是一个prvalue表达式,不能在左边
//一个赋值语句

一个表达式是否可以出现在一个赋值的左边(注意,我们在这里讨论基本数据类型的内置赋值运算符)取决于值类别 lvalue xvalue prvalue ),并且表达式的值类别与其类型无关。


I am beginner in C++. 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.

I quote from the book:

  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.

Considering the code below:

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

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 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 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? Any help is appreciated. Thanks in advance.

解决方案

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.

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.

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

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

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:

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).

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

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

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

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:

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

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:

[ 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 ]

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&&

Let's continue:

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

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.

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

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

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

Finally:

— otherwise, decltype(e) is the type of e.

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;

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).

Concerning this part of your question:

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?

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

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.

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

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