使用 std::reference_wrapper<const T> 来适当地处理 const T& ?

Use std::reference_wrapper<const T> to handle const T& in appropriate?

本文关键字:const 处理 std reference wrapper lt 使用 gt      更新时间:2023-10-16

类拥有U类型的对象。通过一个方法,它使用getter将该对象公开为const U&(不可廉价复制,不需要修改)。

现在,一个客户端想要使用这个API。他希望使用U的实例作为复杂对象的一部分(该对象对更改API对象不感兴趣)。因此,他至少有以下选择:创建一个T类,将const U&作为参数,并创建一个类型为const U&的私有字段,构造函数在其中存储API的实例。这有一个极端的缺点,即使类的实例非常不稳定(例如,没有使用std::vectors的管理),这是不希望的。

不久前,我发现还可以使用std::reference_wrapper<const U>来存储const U&,这不会将这些缺点强加给T类型的实例。

现在的问题是,这样做是否符合预期?这样做是否是一种良好的做法?

在下面和这里,您可以找到使用此策略和所描述类型的工作代码。

#include <iostream>
#include <memory>
class U{
public: 
uint value;
};
class T{
private:
std::reference_wrapper<const U> _u;
public:
T(const U& u)
:_u(u) {}
const U& GetU(){
return _u;
}
};
const U& provideValue(U& u){
return u;
}
int main()
{
U internalApiValue;
internalApiValue.value = 5;
auto apiValue = provideValue(internalApiValue);
T container(apiValue);
std::cout << container.GetU().value;
}

我想如果这不是一个好主意,唯一的选择就是避免const,因为否则我会对这些方法的用户施加很高的限制(公开const U&而不是U&U的方法)?

接口的一个主要问题是T的唯一构造函数采用const U&。这意味着您可以将临时传递给T,并将reference_wrapper留给死对象,因为对象中的const&不会延长临时的生存期。

要解决这个问题,您需要添加一个已删除的构造函数,以阻止您接受临时性。添加

T(const U&&) = delete;

会这么做的。

这应该是你想要做的。它按照预期的方式使用std::reference_wrapper<T>(以可复制和可分配的方式传递引用)。我看不出有什么问题。来自cppreference.com:

std::reference_wrapper是一个类模板,它将引用封装在可复制、可分配的对象中。它经常被用作在标准容器(如std::vector)中存储引用的机制,而标准容器通常不能容纳引用。

我看到的唯一潜在缺点是std::reference_wrapper<T>可能使用起来有点古怪,有些人不熟悉。更常见的问题解决方案可能是只在对象中存储一个指针而不是引用。例如:

class T {
private:
const U* _u;
public:
T(const U& u)
: _u(&u) {}
…
};