std::array
的元素不限于基本类型;它可以是任何对象类型,包括复合类型。因此,可以创建 std::array<指针>
或 std::array<结构体/类>
等。
然而,用结构体或类初始化 std::array
时,新手常踩坑,故本节专门阐述。
定义与赋值:结构体数组
首先定义一个简单的结构体:
struct House
{
int number{};
int stories{};
int roomsPerStory{};
};
定义并赋值:
std::array<House, 3> houses{};
houses[0] = { 13, 1, 7 };
houses[1] = { 14, 2, 5 };
houses[2] = { 15, 2, 4 };
输出:
House number 13 has 7 rooms.
House number 14 has 10 rooms.
House number 15 has 8 rooms.
初始化结构体数组
使用 CTAD 时可省略模板实参,但需显式写出元素类型:
constexpr std::array houses{
House{ 13, 1, 7 },
House{ 14, 2, 5 },
House{ 15, 2, 4 }
};
省略元素类型?——需要额外大括号
若尝试:
constexpr std::array<House, 3> houses{
{ 13, 1, 7 }, // 错误
{ 14, 2, 5 },
{ 15, 2, 4 }
};
会编译失败。
原因在于 std::array
内部定义为:
template<typename T, std::size_t N>
struct array
{
T implementation_defined_name[N]; // 一个 C 风格数组成员
};
编译器将 { 13, 1, 7 }
视为外层 std::array
的第一个成员(即内部 C 数组)的初始化器,导致剩余元素被视为多余,报错。
正确写法:再加一对大括号,用于初始化内部 C 数组:
constexpr std::array<House, 3> houses{{
{ 13, 1, 7 },
{ 14, 2, 5 },
{ 15, 2, 4 }
}};
大括号省略(Brace Elision)
对于标量或已显式类型化的元素,可省略外层大括号:
constexpr std::array<int, 5> arr{ 1, 2, 3, 4, 5 }; // OK
也可写成:
constexpr std::array<int, 5> arr{{ 1, 2, 3, 4, 5 }}; // 同样正确
聚合初始化规则允许在特定情况下省略大括号;为避免思考是否必须,可始终使用双大括号,或先尝试单大括号,若编译器报错再补一层。
完整示例
#include <array>
#include <iostream>
struct House
{
int number{};
int stories{};
int roomsPerStory{};
};
int main()
{
constexpr std::array<House, 3> houses{{
{ 13, 1, 7 },
{ 14, 2, 5 },
{ 15, 2, 4 }
}};
for (const auto& house : houses)
{
std::cout << "House number " << house.number
<< " has " << (house.stories * house.roomsPerStory)
<< " rooms.\n";
}
return 0;
}
更多示例:Student 结构体
#include <array>
#include <iostream>
#include <string_view>
struct Student
{
int id{};
std::string_view name{};
};
constexpr std::array students{
Student{0, "Alex"}, Student{1, "Joe"}, Student{2, "Bob"}
};
const Student* findStudentById(int id)
{
for (auto& s : students)
if (s.id == id) return &s;
return nullptr;
}
int main()
{
constexpr std::string_view nobody{ "nobody" };
const Student* s1{ findStudentById(1) };
std::cout << "You found: " << (s1 ? s1->name : nobody) << '\n';
const Student* s2{ findStudentById(3) };
std::cout << "You found: " << (s2 ? s2->name : nobody) << '\n';
return 0;
}
输出:
You found: Joe
You found: nobody
小测验
问题 1
定义结构体 Item
,含成员 std::string_view name
与 int gold
。用 CTAD 定义并初始化含 4 个 Item
的 std::array
,输出:
A sword costs 5 gold.
A dagger costs 3 gold.
A club costs 2 gold.
A spear costs 7 gold.
问题 2
在问题 1 基础上,不显式写出每个元素的类型,使用额外大括号完成初始化。