数组的元素可以是任何对象类型,既包括基本类型(如 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
对象的常用手段。