当decltype应用于它们时,哪些表达式产生引用类型?

gva*_*vas 30 c++ decltype c++11

我正在阅读C++ Primer,并且当表达式产生对象类型时,以及何时产生对象的引用类型时,我无法理解.

我从这本书中引用:

  1. 当我们将decltype应用于不是变量的表达式时,我们得到>该表达式产生的类型.
  2. 一般来说,decltype为表达式返回一个引用类型,这些表达式产生的对象可以位于赋值的左侧.

考虑以下代码:

int i = 3, *ptr = &i, &ref = i;
decltype(ref + 0) j;
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,表达式"ref + 0"导致添加ref引用的对象的值的固有操作,i和0.因此,通过第一个规则,表达式产生int类型.但是按照第二条规则,因为表达式产生的对象类型可以位于赋值的左侧(在本例中为int),所以decltype不应该产生对int(int&)类型的引用吗?

该书还说,对于以下代码

decltype(*ptr) k;
Run Code Online (Sandbox Code Playgroud)

k的类型为int&而不是int,表达式导致的类型.

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

decltype(a = b) l;
Run Code Online (Sandbox Code Playgroud)

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

我们如何知道哪些表达式产生对象类型以及哪些表达式产生对象类型的引用?

And*_*owl 41

如果不正式就理解这些概念并不容易.引子可能不想让你迷惑并避免引入诸如" 左值 "," 右值 "和" x "之类的术语.不幸的是,这些是了解decltype工作原理的基础.

首先,计算表达式的类型永远不是引用类型,也不const是非类类型的顶级限定类型(例如int constint&).如果表达式的类型为int&int const,则int在任何进一步评估之前立即转换为表达式.

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

5如果表达式最初具有"对T的引用"类型(8.3.2,8.5.3),则T在进行任何进一步分析之前调整类型.表达式表示由参考所表示的对象或功能,且表达为左值x值,这取决于表达.

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

表达方式如此之多.怎么decltype办?那么,决定decltype(e)给定表达式结果的规则e在7.1.6.2/4中规定:

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

- 如果e是未加密码的id-expression或未加括号的类成员访问(5.2.5),decltype(e) 则是由名为的实体的类型e.如果没有这样的实体,或者e命名一组重载函数,程序就会形成错误;

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

- 否则,如果e左值,decltype(e)则是T&,T其类型e;

- 否则,decltype(e)是类型e.

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

这听起来确实令人困惑.让我们尝试逐个分析它.首先:

- 如果e是未加密码的id-expression或未加括号的类成员访问(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
Run Code Online (Sandbox Code Playgroud)

请注意,decltype(e)此处的结果不一定与计算表达式的类型相同e.例如,对表达式的求z值产生了一个类型的值int const,而不是int const&(因为第5/5段的内容&被剥离了,正如我们之前所见).

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

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

这变得复杂了.什么是xvalue?基本上,它是表达式可以属于的三个类别之一(xvalue,lvalueprvalue).一个x值调用与这是一个返回类型的功能时,通常获得的右值引用类型,或作为静态浇铸到的结果右值引用类型.典型的例子是打电话给std::move().

要使用标准中的措辞:

[注意:表达式是xvalue,如果它是:

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

- 对对象类型的右值引用的强制转换,

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

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

在一般情况下,这个规则的效果是名为右值引用被当成左值和无名右值 ,以被视为对象的引用xvalues ; 对函数的右值引用被视为左值,无论是否命名. - 尾注]

因此,例如,表达式std::move(x),static_cast<int&&>(x)std::move(p).first(对于p类型的对象pair)是xvalues.当你申请decltype到一个x值表达式,decltype追加&&到表达式的类型:

int x; // decltype(std::move(x)) = int&&
       // decltype(static_cast<int&&>(x)) = int&&
Run Code Online (Sandbox Code Playgroud)

让我们继续:

- 否则,如果e左值,decltype(e)则是T&,T其类型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
Run Code Online (Sandbox Code Playgroud)

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

int& foo() { return x; } //  decltype(foo()) = int& 
Run Code Online (Sandbox Code Playgroud)

最后:

- 否则,decltype(e)是类型e.

如果表达式不是xvaluelvalue(换句话说,如果它是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
Run Code Online (Sandbox Code Playgroud)

让我们将上述内容应用于您问题中的示例.鉴于这些声明:

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

类型j将是int,因为operator +返回类型的prvalueint.类型k将是int&,因为一元operator *产生左值(见第5.3.1/1段).类型l也是int&,因为结果operator =左值(见第5.17/1段).

关于这部分问题:

但是按照第二条规则,因为表达式产生的对象类型可以位于赋值的左侧(在本例中为int),所以decltype不应该产生对int(int&)类型的引用吗?

你可能误解了书中的这段经文.并非所有类型的对象int都可以位于赋值的左侧.例如,以下分配是非法的:

int foo() { return 42; }

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

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