[basic.def.odr] / 3引用术语“非平凡函数”,其定义在标准(N4140)中找不到。
[basic.def.odr] / 3
变量x,其名称显示为可能已评估的表达式 ex除非应用左值到右值的转换,否则由ex使用 (4.1)到x产生一个不调用的常量表达式(5.19) 任何非平凡的函数,如果x是一个对象,ex是一个元素 表达式e的潜在结果集,其中任何一个 左值到左值的转换(4.1)适用于e,或e是a 丢弃值表达式(第5条)。
答案 0 :(得分:7)
“非平凡功能”是“琐碎的特殊成员功能”的补充。有一些关于什么是普通的和非平凡的默认/复制/移动构造函数,复制/移动赋值运算符或析构函数的定义 - 仅属于特殊成员函数的特征,并决定是否例如在某些情况下需要调用它们。
这些的定义可以在章节§12中找到。
默认构造函数,§12.1/ 4:
默认构造函数是琐事,如果它不是用户提供的,如果:
- 其类没有虚函数(10.3),没有虚基类(10.1)和
- 其类的非静态数据成员没有括号或等于初始化程序,
- 其类的所有直接基类都有简单的默认构造函数和
- 对于类类的所有非静态数据成员(或其数组),每个类都有一个普通的默认值 构造
否则,默认构造函数是非平凡。
复制/移动构造函数,§12.8/ 12:
如果不是,则类X的复制/移动构造函数琐碎 用户提供的,其参数类型列表等同于 参数类型列表的隐式声明,如果
- class
X
没有虚函数(10.3),没有虚基类(10.1),- class
X
没有volatile限定类型的非静态数据成员,- 选择复制/移动每个直接基类子对象的构造函数是微不足道的,
- 对于类型(或其数组)的
X
的每个非静态数据成员,选择复制/移动该成员的构造函数是 琐碎;否则复制/移动构造函数非平凡。
复制/移动赋值运算符,§12.8/ 26:
类
X
的复制/移动赋值运算符琐事如果是 不是用户提供的,它的 parameter-type-list 等同于 参数类型列表的隐式声明,如果
- class
X
没有虚函数(10.3),没有虚基类(10.1),- class
X
没有volatile限定类型的非静态数据成员,- 选择复制/移动每个直接基类的赋值运算符
- 对于类{(1)类的每个非静态数据成员(或其数组),选择赋值运算符来复制/移动该类 会员很琐碎;
否则复制/移动赋值运算符非平凡。
析构函数,§12.4/ 5:
如果析构函数不是用户提供的,则析构函数是琐事,如果:
- 析构函数不是虚拟的,
- 其类的所有直接基类都有简单的析构函数,
- 对于类类的所有非静态数据成员(或其数组),每个类都有一个小问题 析构函数。
否则,析构函数非平凡
答案 1 :(得分:0)
可能是这个小例子将帮助你理解[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();
}
如果您查看标准
中的以下行将lvalue-to-rvalue转换(4.1)应用于x会产生一个不调用任何重要函数的常量表达式(5.19)
这里c满足出现在常量表达式中的要求, 但是将lvalue-to-rvalue转换应用于a 非平凡的功能。
为什么它会调用一个非平凡的函数?
当在未评估的操作数或其子表达式中发生左值到右值转换时,不会访问引用对象中包含的值。否则,如果glvalue具有类类型,则转换从glvalue初始化类型T的临时值,转换结果是临时
的prvalue因此使用类C的复制构造函数创建prvalue,因为复制构造函数是用户声明的,它是非平凡的,因此c不是在这里使用ODR
如果不是用户提供的,则类X的复制/移动赋值运算符是微不足道的,其参数类型列表等效于隐式声明的参数类型列表
我希望这个例子澄清你的怀疑