传递与返回std::array:参数化、模板与性能考量

std::array 对象可以像任何其他对象一样传递给函数。若按值传递,将产生一次昂贵的拷贝,因此我们通常通过(const)引用传参以避免拷贝。

显式指定元素类型与长度

std::array 的元素类型与长度都是其类型信息的一部分,故作为函数形参时必须同时显式给出:

#include <array>
#include <iostream>

void passByRef(const std::array<int, 5>& arr) // 必须写出 <int, 5>
{
    std::cout << arr[0] << '\n';
}

int main()
{
    std::array arr{ 9, 7, 5, 3, 1 }; // CTAD 推导出 std::array<int, 5>
    passByRef(arr);
    return 0;
}

CTAD 尚不能用于函数参数,因此不能写成 std::array 而让编译器推断模板实参。


函数模板:接受任意元素类型或长度的 std::array

若希望函数能处理任意元素类型任意长度std::array,可编写函数模板,把元素类型与长度都参数化:

#include <array>
#include <iostream>

template <typename T, std::size_t N> // 与 std::array 的模板参数声明一致
void passByRef(const std::array<T, N>& arr)
{
    static_assert(N != 0); // 拒绝零长度数组
    std::cout << arr[0] << '\n';
}

int main()
{
    std::array arr{ 9, 7, 5, 3, 1 };  // std::array<int, 5>
    passByRef(arr);                   // 实例化 passByRef<int, 5>

    std::array arr2{ 1, 2, 3, 4, 5, 6 }; // std::array<int, 6>
    passByRef(arr2);                  // 实例化 passByRef<int, 6>

    std::array arr3{ 1.2, 3.4, 5.6, 7.8, 9.9 }; // std::array<double, 5>
    passByRef(arr3);                  // 实例化 passByRef<double, 5>
}

警告
非类型模板参数 N 必须声明为 std::size_t,而非 int;否则模板无法匹配 std::array<T, std::size_t>


仅参数化长度(固定元素类型)

也可只把长度模板化,元素类型显式给出:

template <std::size_t N>
void passByRef(const std::array<int, N>& arr) // 元素类型固定为 int
{
    static_assert(N != 0);
    std::cout << arr[0] << '\n';
}

此时 double 数组将无法匹配,编译报错。


C++20 auto 非类型模板参数

C++20 起可使用 auto 让编译器自动推导非类型模板参数类型:

template <typename T, auto N>
void passByRef(const std::array<T, N>& arr) { /* ... */ }

若编译器支持,可简化模板声明。


在模板长度上静态断言

示例函数:

template <typename T, std::size_t N>
void printElement3(const std::array<T, N>& arr)
{
    std::cout << arr[3] << '\n';
}

若传入 std::array<int, 2>,运行期将越界。
可改用:

  1. std::get<3>(arr)
    编译期检查,越界时报错。

  2. static_assert(N > 3)
    在函数体内断言长度,编译期阻止越界调用。


返回 std::array

std::vector 不同,std::array 不可移动,按值返回会复制整个数组。两种惯例做法:

  1. 按值返回
    当数组不大、元素复制代价低、非性能关键时可直接返回:

    template <typename T, std::size_t N>
    std::array<T, N> inputArray()
    {
        std::array<T, N> arr{};
        // 读入 N 个值
        return arr; // 复制返回
    }
    

    优点:直观、可一次初始化。
    缺点:有拷贝成本,需显式提供模板实参。

  2. 输出参数(out-parameter)
    若拷贝代价高,用非 const 引用参数:

    template <typename T, std::size_t N>
    void inputArray(std::array<T, N>& arr)
    {
        // 填充 arr
    }
    

    无拷贝,但调用语法不直观,无法用于初始化。

  3. 改用 std::vector
    若需动态长度或可移动,返回 std::vector 即可避免拷贝。


小测验

问题 1
补全程序,使其输出:

The array (1, 4, 9, 16) has length 4
The array (h, e, l, l, o) has length 5

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

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

公众号二维码

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