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

标签 c++ c++11 decltype

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

我引用书中的一句话:

  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 和 0 相加的固有操作。因此,按照第一个规则,表达式产生一个 int 类型。
但是按照第二条规则,当表达式产生可以站在赋值左侧的对象的类型(在本例中为 int)时,decltype 不应该产生对 int(int&) 类型的引用吗?

书中还说,对于下面的代码
decltype(*ptr) k;

k 的类型为 int& 而不是 int,即表达式产生的类型。

它还说对于如下代码中的赋值表达式
decltype(a = b) l;

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

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

最佳答案

不正式地理解这些概念是不容易的。入门书可能不想让您感到困惑,并避免引入诸如“lvalue”、“rvalue”和“xvalue”之类的术语。不幸的是,这些是理解如何decltype 的基础。作品。

首先,求值表达式的类型绝不是引用类型,也不是顶级 const - 非类类型的限定类型(例如 int constint& )。如果表达式的类型是 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 to T prior to any further analysis.



这么多的表达。什么decltype做?那么,决定decltype(e)结果的规则对于给定的表达式 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 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).



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

— 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 的结果是该变量的类型。所以
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 段,& 被剥离了,正如我们之前所见)。

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

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



这变得越来越复杂。什么是 xvalue?基本上,它是表达式可以属于的三个类别之一(xvalue、lvalue 或 prvalue)。 xvalue 通常在调用返回类型为右值引用类型的函数时获得,或者作为静态转换为右值引用类型的结果。典型的例子是拨打 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 类型的对象 pair )是 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) is T&, where T is the type of e;



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

对于表达式 e类型 T那是一个左值表达式,decltype(e) yield 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 of e.



如果表达式既不是 xvalue 也不是左值(换句话说,如果它是一个纯右值),则 decltype(e) 的结果只是 e 的类型.未命名的临时变量和文字是纯右值。所以例如:
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 类型的纯右值. k的类型将是 int& ,因为一元operator *产生一个左值(见第 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

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

关于c++ - 当 decltype 应用于它们时,哪些表达式会产生引用类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17241614/

相关文章:

c++ - 什么没有继承到 C++ 派生类?显然, operator= 和一些构造函数实际上是继承的

c++ - 在 SWIG 中将结构从 C++ 函数返回到 Python

c++ - 如何避免在调用此模板时必须使用decltype?

c++ - 使用 fprintf 连续写入字符串

c++ - 未执行代码的性能

c++ - boost socket 的 vector

c++ - 可以使用 C++11 decltype 为现有函数的函数指针创建 typedef 吗?

c++ - 使用 decltype(auto) 的基于范围的 for 循环

c++ - Visual C++ 中的部分构建与完整构建

C++函数不带return返回数据