美文网首页
ODR(Used,Useable)

ODR(Used,Useable)

作者: KinoluKaslana | 来源:发表于2018-12-20 23:01 被阅读0次

    文中绝大部分内容出自个人翻译,从这里,其中个人翻译将在之后发布.

    关于C++ODR

    了解ODR必须知道的:

    • 声明域(Declarative region)作用域(scope)
    • 什么是 潜在求值(potentially evaluated)
    • 什么是 named by an expression
    • 什么是 odr-used
    • 什么是 odr-useable
    • 什么是 odr

    Delarative region 与 scope

    翻译过来实际上,前者就是声明域,后者是作用域</br>
    对于Delarative region,官方对其的描述是:</br>
    代码中引入一个名字的部分;Delarative region是该名字依然有效的最大区域,即可以通过非限定名称来引用同一个实体</br>
    scope:</br>
    scope是值在代码中可能不是连续的部分里一个名字是有效的,为了判断一个声明作用域,最简单的方法就是判断其 潜在的作用域 potential scope,通常,潜在作用域是和作用域是相同的,但是,如果潜在作用域内存在了另一个同名的声明,那么此时潜在作用域中,里面的声明域是不包含外面的声明域的.</br>
    [注记:简单来说,一个名字的Delarative region通常是包含了这个名字所存在的整个区域,potential scope则是自这个名字开始起,一直到这个名字彻底不可见为止,scope则是在potential scope的基础上,如果在其基础上存在了内层的代码块且用了同一个名字进行声明的,则自那个内层名字被引入起直到其potential scope结束的这一块是排除在这个名字的scope之外的]</br>
    [Note:一个声明声明了一个名字并将这个名字引入相应的作用域内,但是friend,详细类型指定符,using指令则会打破这个规则]</br>
    在一个声明域的一组均制定了同一非限定名字的声明,他们应该引用一个实体,或者均引用函数和函数模板,或者当一个声明是非typedef的名字的类名或枚举名字,其他的同样的名字则是引用同一变量,非静态数据成员,或枚举项,或者均为函数或函数模板,则这个类或枚举明就被隐藏</br>
    [注意:一个名称空间和类模板名字必须在一个声明域内唯一]</br>
    intervening declarative region 这个则是表明,在一个declarative region和其的一点间的region,这个region恒等于这个declarative region</br>

    Potentially evaluated

    潜在求值说白了,就是将对一个id表达式进行求值</br>
    其通常包含了一切含有一个id,并且这个id指向的是一个变量实体</br>
    在标准中有如下描述:</br>
    一个表达式e的潜在求值集合是:</br>
    当e为一个id表达式时,集合为e本身</br>
    当e为一个数组下标表达式e1[e2]时,其集合为数组下表操作数(e1,e2)的集合</br>
    当e为一个类成员访问表达式,则集合为对象表达式的集合</br>
    当e为一个成员指针表达式,且第二个操作数是一个常量表达式时,集合就对象表达式的集合</br>
    当e的形式是(e)时,集合为e的集合</br>
    当e是泛左值条件表达式(三目)时,集合是第二和第三个操作数的集合</br>
    当e是一个逗号表达式时,集合是右操作数的集合</br>
    否则集合为空</br>

    Named By an Expression

    这一条主要是针对函数的</br>
    概括上说,就是直接对函数(名字)的操作,除了非显式使用限定符的纯虚函数或是成员指针的形式调用.</br>
    标准中有如下描述:</br>
    一个函数的名称出现在一个表达式中并且其是名称查找的唯一的结果或是一系列重载函数中被选中(经过重载决议后的唯一确定调用的)的那个则就叫被那个表达式命名,除非他是一个没有显示使用限定名称的纯虚函数或是一个使用成员指针的形式</br>
    [注记:这条规定涵盖了对函数取址,使用一个名字调用函数,运算符重载,用户定义的类型转换,new或placement new以及非默认的初始化.一个一个类类型的构造函数选择赋值或移动一个对象是被认为是named by an expression的即使其调用可能被实现忽略]</br>
    一个类的分配(new)和一个解分配(delete)函数被一个new-expression named,就像在expr.newexpr.deleteclass.free说的一样</br>

    odr-used

    odr-used实际上就是指一个变量在潜在求值的集合或函数Named By an Expression</br>

    标准中有如下描述:</br>

    • 一个变量x出现在了一个潜在求值表达式ex中则说明这个表达式被odr-used,除非</br>这个表达式对这个变量进行了左值到右值使其成为一个常量表达式并且期间没有调用任何非trivial的函数,并且</br>如果x是一个对象,ex是表达式e中潜在求值集合的元素,则要求表达式被应用于左值向右值的转换,或者e是一个弃值表达式</br>
    • 一个结构化绑定是odr-used,只要其出现在了潜在求值表达式中</br>
    • *this被odr-used,只要this出现在了潜在求值表达式中(包括在非静态成员函数中的隐式使用)</br>
    • 一个虚函数只要不为纯虚函数则它就被odr-used,一个函数被odr-used只要他被一个潜在求值表达式named by,一个类的非placement new 或一个解分配函数被其构造函数odr-used.一个类的非placement 解分配函数被其析构函数odr-used或被当前定义的虚析构函数查找到.</br>
    • 一个类的赋值操作符重载函数被odr-used在其他类中隐式定义的复制赋值运算符或移动赋值运算符函数,就像[class.copy.assign]中说的.一个类的构造函数被odr-used就像[dcl.init]中说的,一个类的析构函数只要被潜在调用则其就被odr-used</br>

    odr-useable

    这条规定主要是针对一个在某声明域中的局部变量的</br>

    标准中有如下描述:一个本地变量是odr-useable的,只要满足下列条件

    • 这个局部变量不是*this,或在一个封闭类内或在一个非lambda函数形参域中并且,如果最内层作用域是一个函数参数作用域则这个函数是非静态成员函数.
    • 对于每一个intervening declarative region在实体被引入的点和*this被认为随着最内层封闭类或非lambda函数引入的定义作用域则:
      • intervening declarative region是一个块作用域
      • 或intervening declarative region是lambda函数参数作用域并且这个实体被用名字捕获或默认捕获
    • 如果一个局部变量在其非odr-useable的地方被odr-used该程序是ill-formed

    odr

    odr实际上就是单一定义原则,这条规定说明存在某些实体在整个程序中唯一,则该唯一的要求是什么</br></br>
    对于inline来说,每个程序应该完全包含一个inline函数,变量的实体,只要其在非弃值语句外被odr-used,并且每个odr-used之的翻译单元必须存在定义.</br></br>
    对于一个类来说,只要当前翻译单元需要一个该类的完整类型,那么这个类必须在这个翻译单元内定义,对于要求一个类T的完整类型的情况,标准有如下定义:</br></br>

    存在T的对象的定义</br>
    一个T的非静态成员函数被声明</br>
    T作为分配函数的类型或数组元素的类型</br>
    一个泛左值引用了一个类型T并且对这个引用进行了一个左值到右值的类型转换</br>
    一个向T类型的转型</br>
    一个有别于cv void*的表达式使用C++的类型转换方式转向T类型的指针</br>
    对类型T的对象进行的一个类型成员访问运算符</br>
    对类型T使用了typeid或sizeof运算符</br>
    返回类型或形参类型为T的函数被定义或调用</br>
    以T为基类的类型定义</br>
    一个类型为T的左值被赋值(包括复合赋值)</br>
    被alignof包围的类型T的表达式</br>
    抛出对象类型为T,指向或引用T</br>

    对于一个类类型,枚举类型,有外部连接的内联函数内联变量,类模板,非静态函数模板,制约,类模板的静态成员,类模板的成员函数,模板偏特化,以上定义通常应在每个不同的翻译单元中出现,并且需要满足下列要求:</br></br>
    对于以上在不同的(多于一个的)翻译空间内定义的实体D有:</br></br>
    每一个D的定义都应该有相同的一系列token组成,并且</br></br>
    在重载决议和模板偏特化匹配后,在每个D的定义中,相应的名字都应该遵循基本的名称查找原则,并且应该在D的定义范围内指的是同一个实体或应该指向同一实体,除非这个名字指的是:</br></br>

    一个非volatile的const对象并且有内部或无链接,如果这个对象:

    • 在所有D的定义中都有相同的字面类型
    • 是由一个常量表达式初始化的
    • 在任何D的定义内都是非odr-used,并且
    • 有所有D的定义内都有同样的值

      是一个有内部或无链接的常量表达式的引用,这类引用在所有D的定义内都指向同一实体

    并且
    在每个D的定义内,相应的实体应该有同样的语言链接,并且</br></br>
    在每个D的定义内,重载运算符,隐式调用的类型转换函数,构造函数,new,delete 运算符重载,都应该指向同一个函数,或者指向D定义的函数的定义,并且</br></br>
    在每个D的定义内,显式或隐式的使用默认参数的函数调用,其被视为D定义的序列中的token,因此,默认参数也是服从于本段所有的描述(并且,如果默认参数有一个有默认实参的子表达式,那么以上要求也是递归的被要求检查的),并且</br></br>
    如果D有先决条件的调用了了一个函数,或者这个函数包含一个断言或者有一个制约条件,则它是被隐式定义在所有的D的定义都应该使用同样的构建等级和冲突延续模型下,并且</br></br>
    如果D是一个有隐式定义的构造函数的类,则它的行为就像这个构造函数在每个其被ord-used的翻译单元中隐式定义,并且这个在每个翻译单元中隐式的定义对于每个D的子对象而言都应该调用同样的(同一个)构造函数</br></br>
    如果D是一个模板,并且其在多余一个的翻译单元中被定义,则上述要求应该都应用于模板定义中模板作用域中所有的名字,并且依赖其实例化点的名字,如果D的定义满足所有的特点,那么整个程序的行为就像整个程序中只有一个D的定义一样.[注意:一个实体的声明仍然需要在多翻译单元中出现,并且链接仍然作用域这些声明,特别是在类D中可能导致不同声明有截然不同的类型lambda表达式],如果D的定义不满足上述要求,则程序的行为未定义.

    相关文章

      网友评论

          本文标题:ODR(Used,Useable)

          本文链接:https://www.haomeiwen.com/subject/mmcpkqtx.html