1. C++17 中的聚合类 聚合类 就是一种可以直接用初始化列表 {} 进行初始化的类(像 struct 或数组一样)。它通常用来表现“数据集合”,没有太复杂的封装逻辑。
在 C++17 之前,一个类要成为聚合类,必须满足以下严苛条件:
没有用户声明的构造函数 。
没有私有 或保护 的非静态数据成员(必须是 public 的)。
没有虚函数 。
没有基类 。
在 C++17 之前,如果想让一个类继承自另一个类,同时又想保持聚合初始化的特性(即直接用 {} 赋值),是做不到 的。一旦继承了基类,这个类就不再是聚合类了,必须写构造函数来转发参数给基类。
C++17 放宽了限制 ,允许聚合类拥有基类(前提是基类本身也是非虚的聚合类 )。这意味着我们可以更方便地扩展数据结构。现在的规则是:
只要基类也是非虚的、没有私有/保护成员的,就可以继承它。
派生类依然可以不写构造函数,直接使用 {} 初始化。
初始化顺序: 先初始化基类部分,再初始化派生类成员。
综上所述,在C++17中对聚合类的要求如下:
必须是公有继承 (private 或 protected 继承都不行)
不能是虚继承
不能有用户提供的构造函数
不能有私有或受保护的非静态数据成员,并且所有非静态数据成员必须是公有的
不能有虚函数
C++17 引入了关于复合类的类型萃取 std::is_aggregate,可以在编译时检查一个类型是否为聚合类。这对于写模板代码非常有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> #include <type_traits> struct Base { int x; }; struct Derived : public Base { static int y; }; struct NonDerived { int a; NonDerived () {} }; struct NonDerived1 : virtual public Base { int y; }; struct NonDerived2 : public Base { private : int num; }; int main () { std::cout << std::boolalpha; std::cout << "Base is aggregate: " << std::is_aggregate<Base>::value << std::endl; std::cout << "Derived is aggregate: " << std::is_aggregate<Derived>::value << std::endl; std::cout << "NonDerived is aggregate: " << std::is_aggregate<NonDerived>::value << std::endl; std::cout << "NonDerived1 is aggregate: " << std::is_aggregate<NonDerived1>::value << std::endl; std::cout << "NonDerived2 is aggregate: " << std::is_aggregate<NonDerived2>::value << std::endl; return 0 ; }
2. 使用示例 2.1 聚合类初始化 在使用{}初始化聚合类对象的时候,初始化顺序为先初始化基类部分,再初始化派生类成员。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <iostream> #include <string> #include <vector> struct Person { std::string name; int age; }; struct Employee : Person { std::string department; double salary; int employee_id; }; void demo_basic_aggregate () { std::cout << "=== 基本聚合类示例 ===" << std::endl; Employee emp{"张三" , 30 , "技术部" , 15000.0 , 1001 }; Employee emp{{"张三" , 30 }, "技术部" , 15000.0 , 1001 }; std::cout << "姓名: " << emp.name << std::endl; std::cout << "年龄: " << emp.age << std::endl; std::cout << "部门: " << emp.department << std::endl; std::cout << "薪资: " << emp.salary << std::endl; std::cout << "员工ID: " << emp.employee_id << std::endl; std::cout << std::endl; } int main () { demo_basic_aggregate (); return 0 ; }
2.2 结构化绑定 虽然在结构化绑定中直接解包带有基类的聚合比较少见,但聚合初始化为结构化绑定提供了底层支持。下面是C++17 中结构化绑定到数据成员模式的硬性规则:所有非静态 public 数据成员,必须全部来自同一个类:要么全部是派生类直接成员,要么全部来自同一个无歧义的 public 基类;不能一部分在基类(如果是多重继承也只能有一个基类中有数据成员)、一部分在派生类 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <iostream> #include <string> struct RGBColor { int red; int green; int blue; }; struct NamedColor : RGBColor { std::string name; }; struct Alpha { static constexpr uint8_t alpha{255 }; }; struct NameColor : public Alpha{ string name; int hex; }; void demo_structured_binding () { std::cout << "=== 聚合类与结构化绑定 ===" << std::endl; RGBColor color{255 , 128 , 64 }; auto [r, g, b] = color; std::cout << "RGB颜色: (" << r << ", " << g << ", " << b << ")" << std::endl; NamedColor namedColor{{200 , 100 , 50 }, "橙红色" }; std::cout << "命名颜色: " << namedColor.name << " RGB: (" << namedColor.red << ", " << namedColor.green << ", " << namedColor.blue << ")" << std::endl; NameColor c1{{}, "Blue" , 0x0000ff }; auto [name, hex] = c1; std::cout << std::endl; } int main () { demo_structured_binding (); return 0 ; }
C++17 允许聚合类拥有基类,这是一个小但美 的特性。它消除了继承和 POD(纯旧数据)类型之间的隔阂,让 C++ 代码在保持高性能的同时,写起来更加轻量和直观。