std::array 与类类型及大括号省略规则:深度解析固定数组的初始化与聚合特性

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 nameint gold。用 CTAD 定义并初始化含 4 个 Itemstd::array,输出:

A sword costs 5 gold.
A dagger costs 3 gold.
A club costs 2 gold.
A spear costs 7 gold.

问题 2
在问题 1 基础上,显式写出每个元素的类型,使用额外大括号完成初始化。

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"