17-5 通过 std::reference_wrapper 实现的引用数组
在上节课中,我们提到数组可以包含任意对象类型的元素。这包括基本类型对象(如 int)和复合类型对象(如 int 指针)。
#include <array>
#include <iostream>
#include <vector>
int main()
{
int x { 1 };
int y { 2 };
[[maybe_unused]] std::array valarr { x, y }; // an array of int values
[[maybe_unused]] std::vector ptrarr { &x, &y }; // a vector of int pointers
return 0;
}

然而,由于引用并非对象,因此无法创建引用的数组。数组元素还必须可赋值,而引用无法重新定位。
#include <array>
#include <iostream>
int main()
{
int x { 1 };
int y { 2 };
[[maybe_unused]] std::array<int&, 2> refarr { x, y }; // compile error: cannot define array of references
int& ref1 { x };
int& ref2 { y };
[[maybe_unused]] std::array valarr { ref1, ref2 }; // ok: this is actually a std::array<int, 2>, not an array of references
return 0;
}

在本节课中,我们将使用 std::array 作为示例,但这同样适用于所有数组类型。
不过,若需要引用数组,则存在一种变通方案。
std::reference_wrapper
std::reference_wrapper 是位于
关于 std::reference_wrapper 有几点值得注意:
- Operator= 会重新绑定std::reference_wrapper(改变被引用的对象)。
- std::reference_wrapper
会隐式转换为T&。 - 通过get()成员函数可获取T&。当需要更新被引用对象的值时,此特性尤为实用。
以下是一个简单示例:
#include <array>
#include <functional> // for 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; // modify the object in array element 1
std::cout << arr[1] << y << '\n'; // show that we modified arr[1] and y, prints 55
return 0;
}
此示例将输出以下内容:

请注意,我们必须使用 arr[1].get() = 5 而不是 arr[1] = 5。后者存在歧义,因为编译器无法判断我们是想将 std::reference_wrapper
当打印 arr[1] 时,编译器会意识到无法直接输出 std::reference_wrapper
std::ref 和 std::cref
在 C++17 之前,类模板参数推导(CTAD)尚未存在,因此类类型的所有模板参数都需要显式列出。因此,要创建 std::reference_wrapper
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, deduces to std::reference_wrapper<int>
auto cref { std::cref(x) }; // C++11, deduces to 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 对象。

浙公网安备 33010602011771号