【问题标题】:Use std::reference_wrapper<const T> to handle const T& in constructor - appropriate?使用 std::reference_wrapper<const T> 在构造函数中处理 const T& - 合适吗?
【发布时间】:2019-01-16 13:16:57
【问题描述】:

一个类拥有一个U 类型的对象。通过一种方法,它使用 getter 将此对象公开为const U&amp;(不便宜地复制,不需要修改)。

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

不久前,我发现也可以使用std::reference_wrapper&lt;const U&gt; 来存储const U&amp;,这样不会对T 类型的实例造成这些缺点。

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

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

#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&amp; 而不是 U&amp;U 的方法) ?

【问题讨论】:

  • const U * 有什么问题?此外,删除 const 也无济于事:问题在于 reference 本身不可分配。
  • @Quentin const U * 让您了解终身问题。虽然您仍然可以通过引用来解决问题,但这样做确实会违反“规则”。
  • @NathanOliver 我提倡const U &amp; 用于参数,const U * 用于存储,假设遵循“无原始拥有指针”规则。
  • @昆汀啊。这是有道理的。
  • 感谢昆汀的回答和您的意见,其实我没有考虑过。

标签: c++ constructor reference constants


【解决方案1】:

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

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

T(const U&&) = delete;

会这样做的。

【讨论】:

  • 如果我有多个这样的参数,我必须删除所有组合吗? const U&amp;&amp;, const T&amp;&amp;const U&amp;, const T&amp;&amp;,……?
  • @JFFIGK 很有可能。您“需要”(从编译器捕获错误的意义上说)为每个可能需要临时的构造函数都有一个已删除的构造函数。如果你有T(const U&amp; u)T(const U&amp; u, const X&amp;x)T(const U&amp; u, const Y&amp; y const Z&amp; z),那么你需要添加T(const U&amp;&amp;) = deleteT(const U&amp;&amp;, const X&amp;&amp;) = deleteT(const U&amp;&amp;, const Y&amp;&amp;, const Z&amp;&amp;) = delete
  • 在构造函数中已经期望 std::reference_wrapper 怎么样?代码:onlinegdb.com/rkter3nz4 为什么:stackoverflow.com/questions/23973439/…
  • @JFFIGK 那行得通。 std::reference_wrapper 为您执行此操作,因此您可以节省一些工作。如果您这样做,尽管您提交了该接口并公开了内部结构。如果您决定这样做,那可能/将使后者更难改变。
【解决方案2】:

这应该做你想做的事。它以预期使用的方式使用std::reference_wrapper&lt;T&gt;(以使它们可复制和可分配的方式传递引用)。我看不出有什么问题。来自cppreference.com

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

我看到的唯一潜在缺点是std::reference_wrapper&lt;T&gt; 使用起来可能有点尴尬,而且有些人不熟悉。解决您的问题的一个更常见的方法可能是在您的对象中存储一个指针而不是引用。例如:

class T {
private:
    const U* _u;

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

【讨论】:

  • 应该是什么ptr? `常量 T*?我在那里不会有同样的问题吗? (智能指针不合适,因为我不拥有该对象)
  • 您遇到的根本问题是引用本身不是对象(它们只是引用对象的实体)。将它们包装在像 std::reference_wrapper&lt;T&gt; 这样的对象类型中可以解决这个问题。 const T* 也是一种可复制和可分配的对象类型,因此它也不会遇到与普通引用相同的问题……
猜你喜欢
  • 2021-10-22
  • 1970-01-01
  • 2014-07-21
  • 1970-01-01
  • 2021-06-02
  • 1970-01-01
  • 2020-05-18
  • 2012-01-21
  • 1970-01-01
相关资源
最近更新 更多