C++ 什么是平凡函数?

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条) “非平凡函数”是“平凡特殊成员函数”的补充。有关于什么是普通和非普通默认/复制/移动构造函数、复制/移动赋

[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:

如果默认构造函数不是用户提供的,并且:

  • 它的类没有虚拟函数(10.3)和虚拟基类(10.1),并且
  • 其类的任何非静态数据成员都没有大括号或相等的初始值设定项,并且
  • 它的类的所有直接基类都有普通的默认构造函数,并且
  • 对于其类的所有类类型(或其数组)的非静态数据成员,每个此类类都有一个微不足道的默认值 构造器
否则,默认构造函数是非常重要的

复制/移动构造函数,§12.8/12:

类X的复制/移动构造函数如果不是 用户提供,其参数类型列表等同于 隐式声明的参数类型列表,如果

  • X
    没有虚拟函数(10.3)和虚拟基类(10.1),并且
  • X
    没有volatile限定类型的非静态数据成员,并且
  • 选择用于复制/移动每个直接基类子对象的构造函数非常简单,并且
  • 对于类类型(或其数组)的
    X
    的每个非静态数据成员,选择复制/移动该成员的构造函数是 琐碎的
否则,复制/移动构造函数是非常重要的

复制/移动分配运算符,§12.8/26:

X
的复制/移动赋值运算符如果是 非用户提供,其参数类型列表与 隐式声明的参数类型列表,如果

  • X
    没有虚拟函数(10.3)和虚拟基类(10.1),并且
  • X
    没有volatile限定类型的非静态数据成员,并且
  • 选择用于复制/移动每个直接基类的赋值运算符
  • 对于类类型(或其数组)的
    X
    的每个非静态数据成员,选择用于复制/移动该类的赋值运算符 成员是琐碎的
否则,复制/移动赋值运算符是非常重要的

析构函数,§12.4/5:

如果析构函数不是用户提供的,并且:

  • 析构函数不是虚拟的
  • 它的类的所有直接基类都有平凡的析构函数,并且
  • 对于其类中属于类类型(或其数组)的所有非静态数据成员,每个此类类都有一个简单的 析构函数
否则,析构函数是非平凡的


这个小例子可能会帮助您理解[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?