2 Star 1 Fork 0

polarlang / langspecs

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
expr-properties.md 11.91 KB
一键复制 编辑 原始数据 按行查看 历史
zzu_softboy 提交于 2018-05-09 15:08 . 表达式的属性规范完整

表达式属性

值分类

  1. 表达式按照图一所示的分类学进行分类。

               expression
               /        \
           glvalue     rvalue
            /    \     /   \
         lvalue   xvalue  prvalue
     
    1. 一个glvalue分类是一个通过求值去决定一个对象,一个位域或者一个函数的身份的表达式(identity)。
    2. 一个prvalue分类是一个通过求值去初始化一个对象或者位域,或者计算运算符的操作数的值的一个表达式,具体是那种根据出现的上下文去决定。
    3. 一个xvalue分类是一个glvalue,但是这个代表一个对象或者位域等等其资源能够重复利用的程序实体(通常是因为快靠近它们的生命周期的结尾)。[Note: 有些有rvalue引用参与的表达式会产生一个xvalues类型的值,比如:调用一个函数,其返回值是右值类型或者被转换成右值类型。]
    4. 一个lvalue分类是由glvalue分类中那些除去xvalue分类的剩余分类。
    5. 一个rvalue分类是由prvalue分类和xvalue分类组成的。
  2. 每个表达式属于并且只属于这个分类方法中的基础分类的一种:lvaluexvalueprvalue。表达式的这个属性叫做表达式的值分类。[Note: 在本章中,我们将描述8.5章节中的内置运算符求值之后的值的分类和运算符的操作数期望的值的分类。举个例子,内置的赋值运算符期望它的左操作数的值分类是一个lvalue,它的右操作数的值类型是一个prvalue,并且求值之后的产生的结果的值类型是lvalue。用户自定义的重载操作符的各种值的分类由操作符重载函数的形参声明类型和返回值的声明类型来确定。]

  3. [Note: 由于历史原因,左值(lvalue)和右值(rvalue)的叫法是因为它们一个出现在赋值运算符的左边,一个出现在赋值运算符的右边。(现在这个分类的方法现在已经不太合适了,不能描述所有的可能情况);glvalue是通用意义上的左值,prvalues是纯净的右值。xvalue是声明周期即将结束(eXpiring)的左值。尽管它们的名字里面都是带有值(value),但是他们不是针对(value)的分类,而是针对表达式的分类。]

  4. 如果一个表达式满足一下条件之一,那么该表达式就属于xvalue分类:

    1. 不管是隐式或者显式的调用函数,如果其函数的返回值类型是对象的右值引用(rvalue reference)。
    2. 一个通过强制类型转换成对象类型的右值引用(rvalue reference)。
    3. 一个xvalue类型的对象的一个访问类非静态并且不是引用类型的数据成员的类成员访问表达式的分类是xvalue
    4. 一个.*类型的指向类成员的表达式,其中运算符.*的左边操作数的值分类是xvalue,第二个操作数是指向数据成员的指针。

    简单来说,这条规则的效果是命名的右值(rvalue)引用被当成一个左值(lvalue),未命名的对象右值引用被当着xvalue;函数的右值(rvalue)引用,不管是否被命名都被当成xvalue

    例子说明:

    class A
    {
       int m;
    };
    A&& operator+(A, A);
    A&& f();
    
    A a;
    A&& ar = static_cast<A &&>(a);

    在这个例子中表达式f()f().mstatic_cast<A &&>(a)a + a的分类是xvalue

  5. 一个prvalue表达式的结果(result)是表达式在上下文中存入的值。一个prvalue分类的表达式的结果V,有时候被叫做有值V或者命名了值Vpvalue分类的表达式的结果对象(result object)是一个被pvalue分类表达式初始化的对象。一个被用来计算运算符的操作的值的pvalue分类的表达式或者计算具有cv void类型的值时没有结果对象(result object)。[Note: 一个prvalue分类表达式除了用在decltype-specifier里面,一个类类型或者数组类型的pvalue永远具有结果对象。对于丢弃的prvalue表达式分类,一个临时对象被具体化了(todo: 这个是什么意思啊);]表达glvalue分类的结果是该表达式标识的程序对象实体。

  6. 当一个glvalue分类的表达式出现在一个要求操作数的分类是prvalue的表达式中的时候,会对这个传入的glvalue分类的表达式的值进行,lvalue-to-rvaluearray-to-pointer或者function-to-pointer的标准转换,将一个glvalue分类的值转换成prvalue分类的值。[Note:将一个rvalue分类的值绑定到一个lvalue分类的值上,不在这个转换上下文考虑之中。][Note:当一个表达式的值是一个非类类型的时候,表达式的值的cv-qualifiers在转换成prvalue之前就被删除掉了。举个例子来说,一个值类型是const intlvalue表达式可以被值类型为intprvalue分类的表达式所接受。][Note: prvalue分类的值类型不能为位域,当一个位域转换成一个prvalue分类的值类型时,prvalue分类的类型的位域创建之后马上就被做了整数提升转换。]

  7. prvalue分类的表达式用在一个要求操作数的类型是glvalue分类的运算符中时,会使用temporary materialization conversion7.4)将prvalue分类类型的表达式的值转换成xvalue分类类型的表达式的值。

  8. 11.6.3中的引用初始化和15.2中的临时对象相关的指出了lvaluervalue在其他重要的上下文中的的行为。

  9. 除非特别指出,一个prvalue类型的表达式应该是完整类型或者void类型。一个glvalue分类的表达式不能是cv void类型。[Note: glvalue分类的表达式的类型可以是完整类型也可以是不完整的类型。prvalue分类的表达式的值类型如果是类类型或者数组类型,那么可以是cv-qualified类型,其他类型的pvalue分类的表达式的值类型必须是cv-unqualified类型。]

  10. 一个lvalue分类的表达式的值是可修改的(modifiable)除非值类型是const-qualified类型或者是函数类型。[Note:如果程序通过一个不可修改的左值表达式或者右值表达式去修改一个对象,那么程序是不符合规范的。]

  11. 如果一个程序通过一个glvalue表达式去访问一个对象里面存储的值,这个对象的类型不是下面类型中的一个时,程序的行为是未定义的:

    1. 对象的动态类型。
    2. 对象的动态类型cv-qualified版本。
    3. 对象的动态类型的type similar(在7.5中定义)。
    4. 对象的动态类型对应的signed或者unsigned类型。
    5. 对象的动态类型cv-qualified版本类型对应的signed或者unsigned类型。
    6. 一个包含上述类型的元素或者非静态数据成员的聚合或者联合类型。(including, recursively, an element or non-static data member of a subaggregate or contained union)。
    7. 对象的动态类型的基类类型(可能是cv-qualified)。
    8. charunsigned char,或者polar::byte

类型

  1. 如果一个表达式的类型是类型T的引用类型,在进行其他分析之前表达式的类型会被调整成类型T。表达式指定对象或者函数的引用并且表达式的分类是lvalue或者xvalue由表达式本身决定。[Note: 引用类型开始之前或者结束之后进行使用,程序的行为是未定义的。]

  2. 如果一个prvalue分类的表达式初始类型是cv T并且类型T是一个cv-unqualified的非类类型,非数组类型。表达式的类型在进行其他处理之前,会被调整为T

  3. 类型T1和类型T2cv-combined类型T3 similar to T1并且cv-qualification原型是:

    1. 对于所有的i > 0,cvi3是cvi1和cvi2的联合。
    2. 如果cvi3跟cvi1或者cvi2不一样,那么const将添加到所有的cvk3上面,其中0 < k < i。[ Note: Given similar types T1 and T2, this construction ensures that both can be converted to T3. —end note ]
  4. 类型T1的操作数p1和类型T2的操作数p2composite pointer type,至少有一个是指针类型或者成员指针类型或者polar::nullptr_t

    1. 如果p1p2都是空指针常量的话组合结果是polar::nullptr_t;
    2. 如果p1或者p2是空指针常量,组合结果相应的是类型T2或者类型T1;
    3. 如果类型T1或者类型T2,其中一个是指向cv1 void的指针类型并且另一个是指向cv2 T的指针类型,这里的T是对象类型或者void类型。那么他们组合指针类型是pointer to cv12 void,这里的cv12cv1cv2的联合。
    4. 如果类型T1或者类型T2,其中一个是指向noexcept函数类型的指针类型并且另一个是指向函数类型的指针类型,那么组合类型是指向函数类型的指针类型。
    5. 如果T1是指向cv1 C1的指针并且T2是指向cv2 C2的指针,这里C1C2的引用相关的类型,或者C2C1的引用相关的类型。那么组合指针类型是T1T2cv-combined类型或者是T2T1cv-combined类型。
    6. 如果T1是指向类C1的类型为cv1 U1的成员的成员指针类型并且T2是指向类C2的类型为cv2 U2的成员的成员指针类型,这里的C1C2引用相关的类型或者C2C1引用相关的类型。组合指针类型是T2T1cv-combined类型,或者T1T2cv-combined类型。
    7. 如果T1T2similar类型,那么组合指针类型是T1T2cv-combined类型。
    8. 如果其他任何形式的组合出现,那么程序是不符合规范的。

    例子说明:

    typedef void *p;
    typedef const int *q;
    typedef int **pi;
    typedef const int **pci;

    指针类型p和指针类型q的组合指针类型是const void *,指针类型pi和指针类型pci的组合指针类型是const int * const *

上下文依赖

  1. 在有些上下文中,能够出现不被求值的操作数(8.4.7, 8.5.1.8, 8.5.2.3, 8.5.2.7, 10.1.7.2, Clause 17)。一个不被求值的操作数是没有没求值的。[Note: 在一个没有求值的环境中,一个类的非静态成员可能被命名,但是对象或者函数没有命名,所以要求其定义一个要提供。一个未求值的操作数可以看成一个完整的表达式。]
  2. 在一些上下文中,表达式的存在仅仅是为了其副作用。这样的表达式叫做值丢弃表达式(discarded-value expression),标准准换array-to-pointerfunction-to-pointer不会被应用。当且仅当表达式的类型是被volatile-qualified修饰glvalue的分类的值类型并且是下面的情况之一时,表达式的值类型会被进行标准的lvalue-to-rvalue转换:
    1. (expression),这里的expression列出的几种情况之一的表达式。
    2. 标识符表达式(id-expression)。
    3. 下标访问表达式(subscripting)。
    4. 类成员访问表达式(class member access)。
    5. 间接访问表达式(indirection)。
    6. 指向成员的操作表达式(pointer-to-member)。
    7. 三元条件表达式(conditional expression),其中其二个和第三个操作数是这个列表中出现的表达式类型之一。
    8. 逗号表达式(comma expression),其中右边的表达式是这里支持的表达式类型之一。 [Note: 使用重载的运算符会导致一个函数调用;上面的情况只包含内置的操作运算符的情况。]如果表达式(或者转换后的)的分类是prvalue,那么temporary materialization conversion将被应用。[Note: 如果表达式的类型是类类型的左值分类,那么它必须具有一个易变的构造函数去初始化由应用lvalue-to-rvalue转换获取的临时对象。]glvalue分类的表达式将会被求值,但是结果被丢弃。
1
https://gitee.com/polarlang/langspecs.git
git@gitee.com:polarlang/langspecs.git
polarlang
langspecs
langspecs
master

搜索帮助