专业编程基础技术教程

网站首页 > 基础教程 正文

C++20 新特性(5):集合初始化与用户声明构造函数澄清

ccvgpt 2024-10-10 05:02:54 基础教程 64 ℃

当存在用户自定义构造函数时,禁止集合初始化,澄清相关细节

集合初始化是针对集合类(aggregate)的初始化方法,使用大括号进行初始化,主要不要和列表初始化进行混淆。列表初始化同样是使用大括号进行初始化。但集合初始化是直接对成员变量进行初始化,而列表初始化是使用构造函数的。

集合类是有要求的:

C++20 新特性(5):集合初始化与用户声明构造函数澄清

  1. 没有私有或者保护的非静态成员变量
  2. 没有用户自定义或者继承的构造函数
  3. 没有虚基类、私有基类、保护基类
  4. 没有虚函数

C++ 17 标准中已经有了对“没有自定义构造函数“的要求,但要求不够完善,因此出现很多不合理的问题,所以在 C++ 20 中进行了修正。

例子(1):

struct SA 
{
    SA() = delete;
};
SA a1;      // <1> Error,编译不通过,因为默认构造函数被删除,所以不能构造
SA a2 { };  // <2> Error,同上,但C++17可以编译通过!因此C++20修正为编译不通过

当默认构造函数被删除,语句<1>编译不通过,这是很合理的。同样的,语句<2>也是应该编译不通过的。但按照以前的理解,集合类要求没有声明用户自定义的构造函数,那么将默认构造函数删除,算不算用户自定义了构造函数呢?

以前的标准认为是不算,因为确实没有生成新的用户自定义构造函数。但这样语句<1>和语句<2>的差异也不合理。所以后来标准改为,只要是有默认构造函数的相关声明,新增或者删除,或者是保持default,都不能进行集合初始化。

例子(2):

struct SA 
{
private:
    SA() = default;
};
SA a1;      // <1> Error,编译不通过,因为默认构造函数是私有的,所以不能构造
SA a2 { };  // <2> Error,同上,但C++17可以编译通过!因此C++20修正为编译不通过

将默认构造函数改为私有,也不能进行集合初始化。

例子(3):

struct SA 
{
    int m_a { 4 };
    SA() = default;
};
SA a1;      // <1> OK,构造函数不带参数,然后根据定义时的默认值进行赋值
SA a2 { 5 };  // <2> Error,更改了默认值,但C++17可以编译通过!因此C++20修正为编译不通过

上述例子中,显式定义了不带参数的构造函数,那么就是不应该允许有参数的构造方式,因此集合初始化也不应该有效。

例子(4):

struct SA 
{
    int m_a { 4 };
    SA( int ) = delete;
};
SA a1 ( 3 );  // <1> Error,删除了int参数的构造函数,就是显式要求不允许使用int参数进行初始化
SA a2 { 5 };  // <2> Error,应该同上,但C++17可以编译通过!因此C++20修正为编译不通过

和上面的例子类似,更直观一些,直接将int参数的构造函数删除,就不应该支持通过int参数来进行初始化,所以要将集合初始化也改为禁止,保持行为一致。

例子(5):

struct SA 
{
    int m_a ;
    SA( int ) = default;
};
struct SC 
{
    int m_a ;
    SC( int );
};
SC::SC() = default;
SA a1 { 3 };  // <1> C++17 编译通过
SC c1 { 3 };  // <2> C++17 编译不通过

对于这个例子,SA和SC的构造函数是一样的,只不过是直接在类里面定义,还是在类外面定义而已,但C++17的认为这两种情况有区别,一个能编译通过,另一个编译不通过。但这样的差异是不合理的。

因此,在 C++ 20 中,对这些情况都进行了统一,将是否有用户自定义构造函数的标准放宽,只要是有构造函数相关的声明,不管是新增还是删除,还是维持default,都认为是有用户自定义构造函数,都禁止使用集合初始化。(但使用大括号的列表初始化还是支持,不过这时候不是直接对成员变量初始化,还是根据构造函数来初始化)


【往期回顾】

C++20 新特性(4):使用圆括号的多个类成员变量初始化

C++20 新特性(3):指定类成员变量初始化

最近发表
标签列表