通过std::reference_wrapper创建引用的数组
本节阅读量:
在上一课中,我们提到了数组可以包含任何对象类型的元素。这包括基本类型(例如int)的对象和具有复合类型(例如指向int的指针)的对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#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 }; // 指针数组
return 0;
}
|
然而,由于引用不是对象,因此不能创建引用数组。数组的元素也必须是可分配的,并且不能重新放置引用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#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 }; // ok: 这是一个 std::array<int, 2>, 而不是引用的数组
return 0;
}
|
如果需要引用数组,有一个解决方法。
在本课中,将在示例中使用std::array,但这同样适用于所有数组类型。
std::reference_wrapper
std::reference_wrapper是一个标准库类模板,位于<functional>头文件中。它接受类型模板参数T,然后表现得像对T的可修改左值引用。
关于std::reference_wrapper,有几点值得注意:
- Operator=将重置std::reference_wrapper(更改正在引用的对象)。
- std::reference_wrapper<T>将隐式转换为T&。
- get()成员函数可用于获取T&。当我们想要更新被引用对象的值时,这很有用。
下面是一个简单的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#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; // 修改下标 1 处的值
std::cout << arr[1] << y << '\n'; // 修改了 arr[1] 和 y, 打印 55
return 0;
}
|
此示例打印以下内容:
请注意,我们必须使用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>,可以执行以下任一操作:
1
2
3
4
|
int x { 5 };
std::reference_wrapper<int> ref1 { x }; // C++11
auto ref2 { std::reference_wrapper<int>{ x }}; // C++11
|
在长的名称和必须显式列出模板参数的方式之下,创建许多这样的引用包装器可能是一件痛苦的事情。
为了简化操作,c++提供了std::ref()和std::cref()函数作为快捷方式来创建std::reference_wrapper和con std::reference_ wrapper包装的对象。请注意,这些函数可以与auto一起使用,以避免必须显式指定模板参数。
1
2
3
|
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,我们也可以这样做:
1
2
|
std::reference_wrapper ref1 { x }; // C++17
auto ref2 { std::reference_wrapper{ x }}; // C++17
|
但由于std::ref()和std::cref()的拼写较短,因此它们仍然广泛用于创建std::reference_wrapper对象。
17.3 std::array与类类型元素
上一节