数组的元素可以是任何对象类型,既包括基本类型(如 int),也包括复合类型(如指向 int 的指针)。
#include <array>
#include <iostream>
#include <vector>
int main()
{
    int x { 1 };
    int y { 2 };
    [[maybe_unused]] std::array valarr { x, y };   // 存储 int 值的对象数组
    [[maybe_unused]] std::vector ptrarr { &x, &y }; // 存储 int* 的向量
    return 0;
}
但引用不是对象,因而无法直接声明“引用数组”。数组的元素还必须可赋值,而引用本身无法重新绑定。
#include <array>
#include <iostream>
int main()
{
    int x { 1 };
    int y { 2 };
    [[maybe_unused]] std::array<int&, 2> refarr { x, y }; // 编译错误:不能定义引用数组
    int& ref1 { x };
    int& ref2 { y };
    [[maybe_unused]] std::array valarr { ref1, ref2 }; // 正确:实际得到的是 std::array<int, 2>,而非引用数组
    return 0;
}
本节所有示例均使用 std::array,但相关讨论同样适用于其他所有数组形式。
若确实需要“引用数组”,可借助标准库提供的折衷方案。
std::reference_wrapper
std::reference_wrapper 是位于 <functional> 头文件中的类模板。它接受一个类型模板形参 T,行为表现为可修改的 T&(左值引用)。
要点说明:
- 对 std::reference_wrapper使用operator=会重新绑定(reseat)其引用的对象。
- std::reference_wrapper<T>可隐式转换为- T&。
- 成员函数 get()返回T&,用于显式地修改被引用对象的值。
示例:
#include <array>
#include <functional> // std::reference_wrapper
#include <iostream>
int main()
{
    int x { 1 };
    int y { 2 };
    int z { 3 };
    std::array<std::reference_wrapper<int>, 3> arr { x, y, z };
    arr[1].get() = 5; // 修改数组第 1 个元素所引用的对象
    std::cout << arr[1] << y << '\n'; // 显示已修改 arr[1] 与 y,输出 55
    return 0;
}
运行结果:
55
注意:必须写成 arr[1].get() = 5,而不是 arr[1] = 5。后者存在歧义——编译器无法判断我们是要把 std::reference_wrapper<int> 重新绑定到字面量 5(非法操作),还是要修改被引用对象的值。使用 get() 可消除歧义。
在输出 arr[1] 时,编译器发现无法直接打印 std::reference_wrapper<int>,于是将其隐式转换为 int&,从而得以输出,因此此处无需再调用 get()。
std::ref 与 std::cref
在 C++17 引入 CTAD(类模板实参推导)之前,创建类模板对象时必须显式写出所有模板实参。因此,要获得 std::reference_wrapper<int>,可以:
int x { 5 };
std::reference_wrapper<int> ref1 { x };        // C++11
auto ref2 { std::reference_wrapper<int>{ x }}; // C++11
由于名称冗长且必须显式指定模板实参,批量创建引用包装器颇为不便。
为简化使用,标准库提供了 std::ref() 与 std::cref() 两个辅助函数,用于快捷生成 std::reference_wrapper 与 const std::reference_wrapper 包装的对象。配合 auto 可避免手动写出模板实参:
int x { 5 };
auto ref  { std::ref(x) };   // C++11,推导为 std::reference_wrapper<int>
auto cref { std::cref(x) };  // C++11,推导为 std::reference_wrapper<const int>
当然,C++17 起也可借助 CTAD:
std::reference_wrapper ref1 { x };        // C++17
auto ref2 { std::reference_wrapper{ x }}; // C++17
不过,由于 std::ref() 与 std::cref() 书写更简洁,至今仍是生成 std::reference_wrapper 对象的常用手段。
