C++ 什么是平凡函数?
[basic.def.odr]/3引用了术语“非平凡函数”,我在标准(N4140)中找不到它的定义 [basic.def.odr]/3 一个变量x,其名称显示为可能计算的表达式 ex是ex使用的odr,除非将左值转换为右值 (4.1)到x生成一个不调用 任何非平凡函数,如果x是对象,则ex是 表达式e的潜在结果集,其中 左值到右值的转换(4.1)应用于e,或者e是a 废弃值表达式(第5条) “非平凡函数”是“平凡特殊成员函数”的补充。有关于什么是普通和非普通默认/复制/移动构造函数、复制/移动赋值运算符或析构函数的定义,这些特性只与特殊成员函数相关,并决定是否需要在某些情况下调用这些函数 这些定义见第§12章 默认构造函数,§12.1/4: 如果默认构造函数不是用户提供的,并且:C++ 什么是平凡函数?,c++,language-lawyer,c++14,C++,Language Lawyer,C++14,[basic.def.odr]/3引用了术语“非平凡函数”,我在标准(N4140)中找不到它的定义 [basic.def.odr]/3 一个变量x,其名称显示为可能计算的表达式 ex是ex使用的odr,除非将左值转换为右值 (4.1)到x生成一个不调用 任何非平凡函数,如果x是对象,则ex是 表达式e的潜在结果集,其中 左值到右值的转换(4.1)应用于e,或者e是a 废弃值表达式(第5条) “非平凡函数”是“平凡特殊成员函数”的补充。有关于什么是普通和非普通默认/复制/移动构造函数、复制/移动赋
- 它的类没有虚拟函数(10.3)和虚拟基类(10.1),并且
- 其类的任何非静态数据成员都没有大括号或相等的初始值设定项,并且
- 它的类的所有直接基类都有普通的默认构造函数,并且
- 对于其类的所有类类型(或其数组)的非静态数据成员,每个此类类都有一个微不足道的默认值 构造器
- 类
没有虚拟函数(10.3)和虚拟基类(10.1),并且X
- 类
没有volatile限定类型的非静态数据成员,并且X
- 选择用于复制/移动每个直接基类子对象的构造函数非常简单,并且
- 对于类类型(或其数组)的
的每个非静态数据成员,选择复制/移动该成员的构造函数是 琐碎的李>X
X
的复制/移动赋值运算符如果是
非用户提供,其参数类型列表与
隐式声明的参数类型列表,如果
- 类
没有虚拟函数(10.3)和虚拟基类(10.1),并且X
- 类
没有volatile限定类型的非静态数据成员,并且X
- 选择用于复制/移动每个直接基类的赋值运算符
- 对于类类型(或其数组)的
的每个非静态数据成员,选择用于复制/移动该类的赋值运算符 成员是琐碎的李>X
- 析构函数不是虚拟的
- 它的类的所有直接基类都有平凡的析构函数,并且
- 对于其类中属于类类型(或其数组)的所有非静态数据成员,每个此类类都有一个简单的 析构函数
这个小例子可能会帮助您理解[basic.def.odr]/3上下文中的非平凡函数
struct C {
int l;
constexpr C(int _l) : l(_l) { }
constexpr C(const C&c) : q(c.l* 2) { }
};
int main(void) {
constexpr C c(42);
constexpr int m= c.l;
struct K{
int foo() { return c.l; }
} l;
return l.foo();
}
如果你看一下标准中的以下行
将左值到右值的转换(4.1)应用到x会产生一个不调用任何非平凡函数的常量表达式(5.19)
这里c满足出现在常数表达式中的要求,
但是将左值到右值的转换应用到调用
非平凡函数
为什么它调用一个非平凡函数?
当未赋值的操作数或其子表达式中发生左值到右值的转换时,不访问被引用对象中包含的值。否则,如果glvalue具有类类型,则转换副本将从glvalue初始化类型为T的临时值,并且转换的结果是临时值的prvalue
因此,prvalue是使用C类的复制构造函数创建的,因为复制构造函数是用户声明的,所以它不是平凡的,因此这里不使用ODR
类X的复制/移动赋值运算符很简单,如果不是用户提供的,则其参数类型列表相当于隐式声明的参数类型列表
我希望这个例子能澄清您的疑问我只是想理解在[basic.def.odr]/3中使用术语“平凡函数”的原因。您能否给出一个例子,在调用非平凡成员函数(即非平凡构造函数、非平凡析构函数或非平凡复制移动赋值运算符)时,类的常量对象将被视为使用odr?