当前位置: 首页 > news >正文

C++中的Aggregate initialization

C++中的Aggregate initialization

  • Aggregate initialization
    • 定義
    • 語法
    • 專有名詞定義
      • Aggregate
      • Element
  • 範例
    • Animal類別
    • 初始化Animal物件
    • list initialization of std::vector
    • aggregate initialization of std::array
      • default constructor
      • copy constructor
      • move constructor
  • vector為何無法用aggregate initialization

Aggregate initialization

參考Aggregate initialization官方文檔,Aggregate initialization定義如下。

定義

Initializes an aggregate from an initializer list. It is a form of list-initialization(since C++11).

使用 初始化(器)列表 (initializer list) 來初始化一個 聚合型別 (aggregate)。
這是 列表初始化 (list-initialization) 的一種形式(自 C++11 起引入)。
關於list initialization,詳見C++中的List-initialization。

語法

T object = { arg1, arg2, ... };	(1)	
T object { arg1, arg2, ... };	(2)	(since C++11)
T object = { .des1 = arg1 , .des2 { arg2 } ... };	(3)	(since C++20)
T object { .des1 = arg1 , .des2 { arg2 } ... };	(4)	(since C++20)
1,2) Initializing an aggregate with an ordinary initializer list.
3,4) Initializing an aggregate with designated initializers (aggregate class only).

1、2)使用普通的初始化列表(initializer list)來初始化聚合物件(aggregate)。
3、4)C++20 起:使用「指定初始化器」(designated initializers)來初始化聚合物件(僅適用於聚合類別)。

這裡說的聚合物件包括array和符合特定條件的class,用大括號包住array或class的元素後,可用它來就地(in-place)對array或class初始化,aggregate initialization 會把大括號中的每個值 依序對應到array的每個元素或class的每個成員。

專有名詞定義

Aggregate

Aggregate
An aggregate is one of the following types:array types
class types that has
no user-declared constructors
(until C++11)
no user-provided, inherited, or explicit constructors
(since C++11)
(until C++20)
no user-declared or inherited constructors
(since C++20)
no private or protected direct non-static data members
no base classes
(until C++17)
no virtual base classes
no private or protected direct base classes
(since C++17)
no virtual member functions
no default member initializers
(since C++11)
(until C++14)

聚合 (aggregate) 的定義

一個 aggregate 必須是下列型別之一:

  • 陣列型別 (array types)
  • 類別型別 (class types),且需滿足以下限制:
    • 建構子條件
      • C++11 以前:沒有使用者宣告的建構子 (user-declared constructors)
      • C++11 ~ C++20:沒有使用者提供、繼承或 explicit 的建構子
      • C++20 起:沒有使用者宣告或繼承的建構子
    • 成員存取條件
      • 沒有 privateprotected 的直接非靜態資料成員(direct non-static data members)
    • 繼承條件
      • C++17 以前:不能有基底類別
      • C++17 起:沒有虛擬基底類別 (virtual base classes)
      • C++17 起:不能有 privateprotected 的直接基底類別(direct base classes)
      • 沒有虛擬成員函式 (virtual member functions)
    • 成員初始值條件
      • C++11 ~ C++14:不能有 default member initializers

注1:

  • direct non-static data members:在類別裡直接宣告的非靜態資料成員
  • indirect non-static data members:如果是從 基底類別(base class)繼承來的成員,那就不是 direct,而是來自基底。

注2:直接基底類別 (direct base classes)

參考Derived Classes in C++:

A direct base class is the base class from which a 
derived class explicitly inherits. 
An indirect base class is inherited from two or 
more levels up in the class hierarchy. 

直接基類(direct base class)是衍生類別明確繼承的基類。
間接基類(indirect base class)則是位於衍生類別的繼承階層中更上層(兩層或以上)的基類。

Element

Element
The elements of an aggregate are:for an array, the array elements in increasing subscript order, or
for a class, the non-static data members that are not anonymous bit-fields, in declaration order.
(until C++17)
for a class, the direct base classes in declaration order, followed by the direct non-static data members that are neither anonymous bit-fields nor members of an anonymous union, in declaration order.
(since C++17)

Element(聚合元素)

聚合(aggregate)的元素(elements)定義如下:

  1. 對於陣列 (array):
    • 元素就是陣列的每個成員,按照 索引從小到大 的順序。
  2. 對於類別 (class):
    • 直到C++17:聚合的元素是 非靜態資料成員 (non-static data members),排除匿名 bit-field,按宣告順序排列。
    • C++17 起:聚合的元素先是 直接基底類別 (direct base classes),按宣告順序。然後是 直接非靜態資料成員,排除匿名 bit-field 或匿名 union 的成員,按宣告順序排列。

注1:bit-field

參考C++ Bit Fields:

Classes and structures can contain members that occupy less storage than an integral type. These members are specified as bit fields

類別(class)和結構(structure)中可以包含「佔用空間比整型(integral type)更少」的成員。這些成員稱為bit fields。


注2:anonymous union

參考Union declaration:

Anonymous unions
An anonymous union is an unnamed union definition that does not simultaneously define any variables (including objects of the union type, references, or pointers to the union).union { member-specification } ;		

匿名聯合(anonymous union) 是一個沒有名稱的 union 定義,而且在定義的同時不會宣告任何變數(包括該 union 類型的物件、參考或指標)。

範例

我們看完了aggregate initialization的官方文檔,但aggregate initialization相比list initialization帶來了哪些好處呢?編寫一個範例程式來看看:

#include <iostream>
#include <vector>
#include <array>class Animal {
public:Animal(int f, bool t) : feet(f), tail(t) {std::cout << "default construct " << f << ", " << t << std::endl;}Animal(const Animal& a) : feet(a.feet), tail(a.tail) {std::cout << "copy construct " << a.feet << ", " << a.tail << std::endl;}Animal(Animal&& a) {std::cout << "move construct " << a.feet << ", " << a.tail << std::endl;feet = a.feet;tail = a.tail;a.feet = 0;a.tail = false;}private:int feet;bool tail;
};int main()
{std::cout << "a1" << std::endl;Animal a1 = { 4, true };std::cout << "==========================================" << std::endl;std::cout << "a2" << std::endl;Animal a2 = a1;std::cout << "==========================================" << std::endl;std::cout << "a3" << std::endl;Animal a3 = std::move(a2);std::cout << "==========================================" << std::endl;std::cout << "list initialization, default constructor" << std::endl;std::vector<Animal> vec_animal = { {4, true}, {2, false} };std::cout << "==========================================" << std::endl;std::cout << "list initialization, copy constructor" << std::endl;std::vector<Animal> vec_animal2 = { a1, a2 };std::cout << "==========================================" << std::endl;std::cout << "list initialization, move constructor" << std::endl;std::vector<Animal> vec_animal3 = { std::move(a1), std::move(a2) };std::cout << "==========================================" << std::endl;std::cout << "aggregate initialization, default constructor" << std::endl;std::array<Animal, 2> arr_animal = { { {4, true}, {2, false} } };// std::array<Animal, 2> arr_animal = { {4, true}, {2, false} }; // invalidstd::cout << "==========================================" << std::endl;std::cout << "aggregate initialization, copy constructor" << std::endl;std::array<Animal, 2> arr_animal2 = { { a1, a2 } };// std::array<Animal, 2> arr_animal2 = { a1, a2 }; // validstd::cout << "==========================================" << std::endl;std::cout << "aggregate initialization, move constructor" << std::endl;std::array<Animal, 2> arr_animal3 = { { std::move(a1), std::move(a2) } };// std::array<Animal, 2> arr_animal3 = { std::move(a1), std::move(a2) }; // validreturn 0;
}

Animal類別

程式中首先定義了一個名為Animal的class:

class Animal {
public:Animal(int f, bool t) : feet(f), tail(t) {std::cout << "default construct " << f << ", " << t << std::endl;}Animal(const Animal& a) : feet(a.feet), tail(a.tail) {std::cout << "copy construct " << a.feet << ", " << a.tail << std::endl;}Animal(Animal&& a) {std::cout << "move construct " << a.feet << ", " << a.tail << std::endl;feet = a.feet;tail = a.tail;a.feet = 0;a.tail = false;}private:int feet;bool tail;
};

當中定義了default, copy, move三種建構子,每種建構子在被調用時皆會有相對應的輸出。在接下來的程式中我們便可以透過輸出來判斷究竟是何種建構子正在被調用。

初始化Animal物件

接下來便實際透過default, copy, move三種constructor來分別初始化a1, a2, a3三個物件:

    std::cout << "a1" << std::endl;Animal a1 = { 4, true };std::cout << "==========================================" << std::endl;std::cout << "a2" << std::endl;Animal a2 = a1;std::cout << "==========================================" << std::endl;std::cout << "a3" << std::endl;Animal a3 = std::move(a2);std::cout << "==========================================" << std::endl;

這部分的運行結果如下:

a1
default construct 4, 1
==========================================
a2
copy construct 4, 1
==========================================
a3
move construct 4, 1
==========================================
  • 注意到因為Animal類別有自定義的建構子,不符合聚合 (aggregate) 的條件,所以無法對它使用aggregate initialization。此處a1的初始化方式是List-initialization,會呼叫default constructor完成初始化
  • a2透過=運算子呼叫copy constructor完成初始化
  • a3=運算子右邊的是經過std::move後的右值,所以move constructor會被呼叫

list initialization of std::vector

接下來用以下三種方式初始化std::vector<Animal>

std::cout << "list initialization, default constructor" << std::endl;
std::vector<Animal> vec_animal = { {4, true}, {2, false} };
std::cout << "==========================================" << std::endl;std::cout << "list initialization, copy constructor" << std::endl;
std::vector<Animal> vec_animal2 = { a1, a2 };
std::cout << "==========================================" << std::endl;std::cout << "list initialization, move constructor" << std::endl;
std::vector<Animal> vec_animal3 = { std::move(a1), std::move(a2) };
std::cout << "==========================================" << std::endl;

運行結果如下:

list initialization, default constructor
default construct 4, 1
default construct 2, 0
copy construct 4, 1
copy construct 2, 0
==========================================
list initialization, copy constructor
copy construct 4, 1
copy construct 0, 0
copy construct 4, 1
copy construct 0, 0
==========================================
list initialization, move constructor
move construct 4, 1
move construct 0, 0
copy construct 4, 1
copy construct 0, 0
==========================================

注意到在程式中明明每個vector都只有兩個元素,但是在運行結果中,Animal的constructor卻被呼叫了四次,這是為什麼呢?這部分的解析已獨立成文,詳見C++中的List-initialization。

aggregate initialization of std::array

因為std::vector只能使用list initialization,而list initialization本身的特性則會導致同一個元素被建構出來後還要再被多複製一次。因此才會看到上面大括號中明明只有兩個元素,建構子卻被呼叫四次的現象。

接著來看看相比list initialization,aggregate initialization有何優越之處。

default constructor

std::cout << "aggregate initialization, default constructor" << std::endl;
std::array<Animal, 2> arr_animal = { { {4, true}, {2, false} } };
// std::array<Animal, 2> arr_animal = { {4, true}, {2, false} }; // invalid
std::cout << "==========================================" << std::endl;

這個範例中用到了std::array,因為std::array屬於陣列型別 (array types),滿足聚合 (aggregate)的條件,因此可以套用aggregation initialization。

在初始化arr_animal時用了三對括號,比vector list initialization多了一對,其實這跟std::array的底層定義有關。根據17.4 — std::array of class types, and brace elision:

A std::array is defined as a struct that contains a single C-style array member (whose name is implementation defined), like this:
template<typename T, std::size_t N>
struct array
{T implementation_defined_name[N]; // a C-style array with N elements of type T
}

可以看到std::array本質上是一個包含單個元素的struct,而該元素則是一個T型別的C語言的陣列。

注意到外層的struct滿足聚合 (aggregate)對類別型別(class types)的條件,所以外層struct是為聚合(aggregate),可套用aggregate initialization;內部的implementation_defined_name則是陣列型別 (array types),因此它也是聚合 (aggregate),也可套用aggregate initialization。

所以初始化arr_animal時用的三對括號的作用分別是:

  • 最外層的括號是用aggregate initialization來初始化std::array所代表的struct本身。因為該struct只有implementation_defined_name一個元素,所以最外層括號內也只有一個元素
  • 中層括號的作用是用aggregate initialization來初始化implementation_defined_name。中層括號內的兩個元素會被用來初始化implementation_defined_name陣列所包含的兩個元素
  • 最內層的括號則是用list-initialization來初始化Animal,括號內的兩個元素會被當作參數傳給Animal的default constructor

這部份的運行結果如下:

aggregate initialization, default constructor
default construct 4, 1
default construct 2, 0
==========================================

可以看到相比於vector的list initialization,constructor被呼叫的次數減半,也就是說,對於一個Animal物件,constructor只被呼叫了一次,帶來了性能上的提升。

copy constructor

std::cout << "aggregate initialization, copy constructor" << std::endl;
std::array<Animal, 2> arr_animal2 = { { a1, a2 } };
// std::array<Animal, 2> arr_animal2 = { a1, a2 }; // valid
std::cout << "==========================================" << std::endl;

這段程式中仍用了多層括號:

  • 最外層的括號是用aggregate initialization來初始化std::array所代表的struct本身。因為該struct只有implementation_defined_name一個元素,所以最外層括號內也只有一個元素
  • 內層括號的作用是用aggregate initialization來初始化implementation_defined_name。內層括號內的兩個元素會被用來初始化implementation_defined_name陣列所包含的兩個元素
  • 用Copy-initialization分別由a1a2初始化得到兩個Animal物件,過程中會呼叫Animal的copy constructor

這部份的運行結果如下:

aggregate initialization, copy constructor
copy construct 0, 0
copy construct 0, 0
==========================================

注:編譯器允許programmer省略最外層的括號,因此以下寫法也是有效的:

std::array<Animal, 2> arr_animal2 = { a1, a2 }; // valid

move constructor

std::cout << "aggregate initialization, move constructor" << std::endl;
std::array<Animal, 2> arr_animal3 = { { std::move(a1), std::move(a2) } };
// std::array<Animal, 2> arr_animal3 = { std::move(a1), std::move(a2) }; // valid

這段程式中也使用了多層括號,其意涵如下:

  • 最外層的括號是用aggregate initialization來初始化std::array所代表的struct本身。因為該struct只有implementation_defined_name一個元素,所以最外層括號內也只有一個元素
  • 內層括號的作用是用aggregate initialization來初始化implementation_defined_name。內層括號內的兩個元素會被用來初始化implementation_defined_name陣列所包含的兩個元素
  • std::move(a)std::move(b)這兩個右值分別被當作Animal的move constructor的參數,用於建構兩個Animal物件

這部份的運行結果如下:

aggregate initialization, move constructor
move construct 0, 0
move construct 0, 0

注:編譯器允許programmer省略最外層的括號,因此以下寫法也是有效的:

std::array<Animal, 2> arr_animal3 = { std::move(a1), std::move(a2) }; // valid

注意我們不能將=右側的{ std::move(a1), std::move(a2) }寫成std::move({ a, b })

std::array本來可以從大括號直接初始化的,但是如果我們把=右側的東西寫成std::move({ a, b }),則它的型別會變成 std::initializer_list<Animal>&&

因為std::array沒有接受 std::initializer_list 為參數的建構子,所以無法由std::move({ a, b })初始化std::array

vector為何無法用aggregate initialization

最後來探討一下std::vector為何只能用list initialization,無法用aggregate initialization。

因為aggregate initialization只適用於聚合類型,而vector不是聚合類型,所以無法直接由大括號「就地」建構std::vector<Animal>內部的元素。

在建構std::vector的過程中,首先會建構一個initializer_list,再呼叫std::vector的接受initializer_list的建構子。在呼叫該建構子的過程中,因為initializer_list的iterator是const的,所以必須一一將initializer_list中的元素複製給std::vector(無法move),最終導致每個元素都需要被多複製一次。

http://www.dtcms.com/a/525013.html

相关文章:

  • 鸿蒙Harmony实战开发教学(No.8)-Hyperlink超链接组件基础到进阶篇
  • Ubuntu开启SSH
  • 郑州营销网站托管和淘宝同时做电商的网站
  • 删除网站备案百度搜索风云榜手机版
  • 枣庄市网站建设跨境电商亚马逊开店流程
  • 做网站还有市场吗苏州住建网站
  • 网站仿做软件wordpress 页面显示最新文章
  • C# 取消机制(CancellationTokenSource/CancellationToken)
  • 散列查找及性能分析的应用
  • 百日挑战之单词篇(第二天)
  • 香港首位范·克莱本国际钢琴大赛冠军 沈靖韬 签约环球音乐 即将推出全新独奏专辑
  • 网站托管工作室wordpress防止f12
  • 传播网站建设建筑涂料网站设计
  • 详解C++中的字符串流
  • 中建西部建设广通讯网站小程序制作需要多少钱一个
  • 石家庄有哪些公司可以做网站南通网站优化找哪家
  • 全面掌握PostgreSQL关系型数据库,pgAdmin 图形化客户端,笔记12
  • 怎么做地方门户网站桂林市区
  • (论文速读)开放词汇3D场景理解的掩蔽点-实体对比
  • 做网站是经典网站设计作品
  • C++进阶:继承
  • 网站 建设运行情况网站移动转换
  • 如何上传ftp网站程序普像工业设计网站
  • 做响应式网站的公司如何做与别人的网站一样的
  • pytorch下对各种超参调整效果
  • 做网站会遇到的问题title 镇江网站建设
  • 怎么做网站底部版权信息在哪可以接企业网站建设的活
  • 聊城网站建设服务好赣州网站制作较好的公司
  • 今日行情明日机会——20251024
  • pip常用命令