StupidBeauty
Read times:2760Posted at: - no title specified

C++国际规范草案翻译:隐式删除的特殊成员函数, Implicitly-Deleted Special Member Functions

作者:Douglas Gregor, Apple

文档编号:N2924=09-0114

日期:2009-07-17

主题:编程语言C++,核心工作组

回复地址:Douglas Gregor<doug.gregor@gmail.com>

介绍

概念(Concepts) i ,向C++0x工作文档中引入了名为隐式删除的特殊成员函数的标记。去掉了概念之后,我们仍然希望保留这个并非只与概念相关的变更。这个变更的关键之处在于,对于一个隐式声明的特殊成员函数(默认构造函数、复制构造函数、析构函数、复制赋值操作符),如果它的定义会不符合规范(ill-formed),则,它会被隐式地定义为删除的(deleted (即,带上= delete)。例如:

struct HasReference {

int & ref;

//隐式声明以下函数:

//HasReference() = delete;

//HasReference(const HasReference&);

//HasReference& operator=(const HasReference&);

//HasReference();

} ;

引入这个变更的目的在于,在模板参数的演绎过程中,如果发现了一个隐式声明的特殊成员函数,并且该函数的定义会编译失败,则确保这个演绎过程失败。这个问题 ii ,是在概念的范畴中出现的,std::pair<const int, int>这个类型,会拥有一个隐式声明的复制赋值操作符,以使得它满足CopyAssignable概念的要求。然而,在这种情况下,这个复制赋值操作符是不符合规范的,(因为有一个const成员),这样,就会引起未预期的错误。将这个复制赋值操作符标记为已删除的,就使得,它不再满足CopyAssignable概念的要求。即使是不再采用概念这个特性,但是,对于扩展后的替换失败并不是错误(SFINAE)规则,这个问题仍然存在。

这个草案,与以下两个草案等价:由N2773做出的非概念的变更;和,最新的包含概念的工作文档 (N2857)。

编辑说明:我们不需要退回去使用概念这个词引入之前的说法然后使用这个文档中的草案词汇,实际上,我们可以去掉[class.conv.fct]和[class.inhctor]中的与概念相关的变更,这样,在[special]表达的意思上,具有相同的效果。

第12章 特殊成员函数 [special]

12.1 构造函数 [class.ctor]

类X的默认构造函数,指的是,类X中,可以不带参数来调用的构造函数。如果用户未给类X定义构造函数,则,会隐式声明一个不带参数的构造函数。隐式声明的默认构造函数,是对应的类的内联公有成员。默认构造函数,如果是被隐式声明的,并且满足以下条件,则称它为普通(trivial)的:

— 它的类,不包含虚函数([class.virtual]),不包含虚基类([class.mi]),并且

— 它的类的所有直接基类,都拥有普通的默认构造函数,并且

— 它的类中,对于所有其类型为类(或者由类组成的数组)的非静态数据成员,数据成员所对应的每个类,都拥有普通的默认构造函数。

如果满足以下条件,则,类X的隐式声明的默认构造函数会被删除:

X是一个联合体类型的类,其中有一个变量成员,拥有不普通的默认构造函数,

某个非静态的数据成员,是引用类型,

某个非静态的被const修饰的数据成员(或者这种类型组成的数组),不拥有用户定义的默认构造函数,或

某个非静态的数据成员,或直接基类,或虚基类,其类型计为M(或者由M组成的数组),对于M,它没有默认构造函数,或者,在对M的默认构造函数进行重载解析([over.match])的过程中,产生了歧义,或者,产生了一个被删除的函数,或者产生了一个从隐式声明的默认构造函数中无法访问到的函数。

类中,未由用户定义的默认构造函数,会在该构造函数被用来([basic.def.odr])创建该类的一个对象时([intro.object]),被 隐式定义 。隐式 定义 的默认构造函数,或者,显式标记为默认的默认构造函数,会像用户所编写的默认构造函数那样,对该类进行初始化,它们会使用一个空白的 成员初始化列表 mem-initializer-list([class.base.init]) )和空白的函数体。 如果隐式定义的复制构造函数被显式标记为默认的,同时,对应的隐式声明又被删除,则,该程序是不符合规范的。 如果用户编写的默认构造函数满足常量表达式(constexpr)构造函数([decl.constexpr])的要求,则,隐式定义的默认构造函数就是常量表达式。在对某个类的未由用户编写的默认构造函数进行隐式定义之前,该类的基类和非静态的数据成员的未由用户编写的默认构造函数都要先被隐式定义。[ 注意 :隐式声明的默认构造函数,拥有一个 异常规范 exception-specification ([except.spec]) )。显式标记为默认的定义,不拥有隐式的 异常规范 注意内容结束 ]

12.4 析构函数 [class.dtor]

果某个类未由用户声明其析构函数,则会隐式声明一个析构函数。隐式声明的析构函数,是该类的内联公有成员。对于一个析构函数,如果它是隐式声明的,并且满足以下条件,则称它为普通的:

— 这个类的所有直接基类都拥有普通的析构函数,并且

— 对于这个类中所有非静态的数据成员,如果其数据类型是某个类类型(或者由类类型组成的数组),则,对于这样的每个类类型,都拥有一个普通的析构函数。

类X的隐式声明的析构函数,如果满足以下条件,则会被删除:

X是一个联合体类型的类,其中有一个变量成员拥有一个不普通的析构函数,

某个类类型的非静态数据成员,其类型 为M(或者是由M类型组成的数组),对于M,它拥有一个被删除的析构函数,或者,它的析构函数无法通过这个类的隐式声明的析构函数访问到,或

某个直接基类,或虚基类,拥有一个被删除的析构函数,或者,它的析构函数无法通过这个类的隐式声明的析构函数访问到。

类中,隐式声明的析构函数,会在它被用来销毁这个类类型的某个对象时([basic.stc]),被 隐式定义 。对于一个程序, 如果它的隐式定义的析构函数被显式标记为默认的,而对应的隐式声明却又被删除了 则它是不符合规范的

对于某个类,在对它的隐式声明的析构函数进行隐式定义之前,它的基类和非静态数据成员的所有隐式声明的析构函数都要先被隐式定义。[ 注意 :隐式声明的析构函数,拥有一个 异常规范 ([except.spec])。— 注意内容结束 ]

12.8 对类对象进行复制 [class.copy]

如果类定义中未显式声明一个复制构造函数,则,会隐式地声明一个。因此,对于下列类定义

struct X {

  X ( const X & , int ) ;

} ;

会隐式声明一个复制构造函数。如果,用户声明的构造函数日后被定义为

X :: X ( const X & x, int i = 0 ) { / ... / }

则,对于X的复制构造函数的任何使用都是不符合规范的,因为,这会产生歧义;不需要做额外的诊断。

类X的隐式声明的复制构造函数,在下列条件下,其形式为

X :: X ( const X & )

条件为

— 类X的每个直接基类或虚基类B,都拥有一个复制构造函数,并且其第一个参数是const B&类型或const volatile B&类型,并且

— 对于X的每个为类类型的非静态数据成员,将其类型记为M(或者由M组成的数组),都拥有一个复制构造函数,并且其第一个参数是const M&类型或const volatile M&类型。 1

否则,隐式声明的复制构造函数的形式会是

X :: X ( X & )

隐式声明的复制构造函数,是该类的内联公有成员。 对于类X,如果X满足以下条件,则,它的隐式声明的复制构造函数,会被删除:

它是一个联合体类型的类,并且,其中一个变量成员拥有不普通的复制构造函数,

它拥有一个非静态的数据成员,类型为类M(或者由M组成的数组),并且,在对M的复制构造函数进行重载解析([over.match])时,产生出了歧义,或者,产生出了一个被删除的函数,或者,产生了一个从隐式声明的复制构造函数中无法访问到的函数,使得该数据成员不可被复制,或者

它拥有某个直接基类或虚基类B,并且,在对B的复制构造函数进行重载解析([over.match])时,产生出了歧义,或者,产生了一个被删除的函数,或者,产生了一个从隐式声明的复制构造函数中无法访问到的函数。

未由用户定义的复制构造函数,当被用来以该类的某个对象的一个副本或该类的某个子代类的对象的一个副本来初始化该类的一个对象时,会被 隐式定义 2 [ 注意 :复制构造函数,即使实际实现中省略掉了对于它的使用( [class.temporary] ),仍然会被隐式定义。— 注意内容结束 ] 对于一个程序, 如果 某个类的 隐式定义的复制构造函数被显式标记为默认的,而对应的隐式声明却被删除 ,则认为该程序是不符合规范的

在未由用户提供的复制构造函数被隐式定义之前,对于所有的直接基类、虚基类和非静态的类类型的数据成员,都要先隐式定义其未由用户提供的复制构造函数。[ 注意 :隐式定义的复制构造函数,拥有一个 异常规范 ([except.spec])。显式标记为默认的定义,则不具有隐式的 异常规范 注意内容结束 ]

如果类定义中未显式声明一个复制赋值操作符,则会 隐式 声明一个。针对类X隐式声明的复制赋值操作符,如果满足以下条件,则会具有以下形式

X & X :: operator = ( const X & )

条件为

— X的每个直接基类B,都拥有一个复制赋值操作符,且其参数是const B&类型或const volatile B&类型或B类型,并且

— 对于X中的所有非静态的类类型的数据成员,将其类型记为M(或者由M类型组成的数组),每个这种类类型,都拥有一个复制赋值操作符,并且其参数为const M&类型、const volatile M&类型或M类型。 3

否则,隐式声明的复制赋值操作符会具有以下形式

X & X :: operator = ( X & )

X类的隐式声明的复制赋值操作符,其返回值类型为X&;它返回的是,针对其发起此赋值操作符调用的那个对象,也就是,被赋值的目标对象。隐式声明的复制赋值操作符,是该类的内联公有成员。 对于类X,如果它满足以下关系,则,隐式声明的复制赋值操作符会被删除:

X是一个联合体类型的类,并且,其中的某个变量成员拥有一个不普通的复制赋值操作符,

它的某个非静态数据成员,类型为常量的(const)非类类型(或该类型组成的数组),或

它的某个非静态的数据成员,为引用类型,或

它的某个非静态的数据成员,为类类型,其类型记为M(或者由M类型组成的数组),在对M类型的复制赋值操作符进行重载解析([over.match])时,产生了歧义,或者产生了一个被删除的函数,或者产生了一个无法从隐式声明的复制赋值操作符中访问到的函数,因而使得M类型不可被复制,或者

它的某个直接基类或虚基类B,在对B的复制赋值操作符进行重载解析([over.match])时,产生了歧义,或者产生了一个被删除的函数,或者产生了一个无法从隐藏声明的复制赋值操作符访问到的函数,因而使 B不可被复制。

因为,对于一个类,如果用户未声明其复制赋值操作符,则会隐式声明一个复制赋值操作符,所以,基类的复制赋值操作符一定会被继承类的复制赋值操作符隐藏掉([over.ass])。即使使用一个 using 声明([namespace.udecl]),从基类中导入一个可接受继承类的复制赋值操作符 所需参数类型的赋值操作符,也不会被认为是显式地声明了一个复制赋值操作符,因此,也 不会阻止对于继承类的复制赋值操作符的隐式声明;在继承类中,由 using 声明所引入的那个操作符,会被继承类的隐式声明的复制赋值操作符所隐藏掉。

对于一个未由用户提供的复制赋值操作符,当它被用来将该类型的某个值或者该类型的继承类型的某个值赋于给该类型的某个对象时,会被 隐式定义 。对于一个程序, 如果其中某个类中,该隐式定义的复制赋值操作符被显式标记为默认的, 但是,对应的隐式声明却已被删除 ,则称它为不符合规范的

对于某个类,在未由用户提供的复制赋值操作符被隐式定义之前,该类的所有直接基类和所有非静态的数据成员中,未由用户提供的复制赋值操作符,都必须先被隐式定义。[ 注意 :隐式声明的复制赋值操作符,拥有一个 异常规范 ([except.spec])。显式被标记为默认的定义,则不拥有隐式的 异常规范 。— 注意内容结束 ]

参考书目

未知美人

三角函数

冯静

圆角矩形

未知美人

热辣性感的女兵

高圆圆

柳岩,妲己

1 这说明,隐式声明的复制构造函数中,引用类型的参数,无法被绑定到一个易失型(volatile)的左值(lvalue);参考[diff.special]。

2 参考[dcl.init]以了解更多关于直接初始化和复制初始化的信息。

3 这说明,隐式声明的复制赋值操作符中,引用类型的参数,无法被绑定到一个易失型(volatile)的左值(lvalue);参考[diff.special]。

i D. Gregor、B. Stroustrup、J. Widman和J. Siek.概念(concepts )术语草案(修订版本9)。技术报告(Technical Report)N2773=08-0283,ISO/IEC JTC 1,信息技术,子委员会SC 22,编程语言C++,2008年9月。

ii D.Gregor.隐式赋值和构造。 http://conceptgcc.wordpress.com/2006/05/04/implicit-assignments-and-construction/ ,2006年5月。

Your opinions

Your name:Email:Website url:Opinion content:
- no title specified

HxLauncher: Launch Android applications by voice commands