在类中存储对对象的 const 引用

Posted

技术标签:

【中文标题】在类中存储对对象的 const 引用【英文标题】:Storing const reference to an object in class 【发布时间】:2016-06-16 16:07:09 【问题描述】:

这听起来像是一个基本问题,但我没有找到任何全面的答案,所以在这里。考虑这段代码sn-p:

struct A 
    const std::string& s;
    A(const std::string& s) : s(s) 
;

int main() 
    A a("abc");
    std::cout << a.s << std::endl;
    return 0;

Demo.

据我所知,这是UB。字符串文字“abc”在构造函数中绑定到const std::string&amp;,创建一个临时字符串对象。它也被绑定到a.s,一旦a被构造,它就会被销毁。也就是说,const 引用不能链接生命周期延长。悬空参考,繁荣。在这种特殊情况下,我在 ideone.com 上根本看不到任何输出,但任何事情都可能发生(记住 velociraptors)。

好的,这个很清楚。但是,如果这实际上是我们的意图:我们想要存储对对象的 const 引用呢?对现有的,而不是临时的?这听起来像是一项非常自然的任务,但我只想出了一个(几乎)自然的解决方案。通过std::reference_wrapper而不是通过引用接受构造函数的参数:

    A(std::reference_wrapper<const std::string> r) : s(r) 

由于std::reference_wrapper 已经从临时对象中删除了构造函数:

reference_wrapper( T&& x ) = delete;

这就像预期的那样工作。然而,这不是很优雅。我能想到的另一种方法是接受转发引用T&amp;&amp; 并拒绝除带有std::enable_if 的常量左值字符串之外的所有内容。我认为这更不优雅。

还有其他方法吗?

UPD 另一个问题:这是std::reference_wrapper 的合法用法,还是被认为过于具体?

【问题讨论】:

A 不能只有普通的std::string 的任何原因? A::s 可能会悬空,即使它接收到左值并且引用破坏了赋值运算符。这有用例还是纯粹的学术? A(const std::string &amp;&amp;) = delete; 可能会做你想做的事。我仍然认为这不是一个好主意。 @nwp 这在实用程序类中可能很有用,我们控制其实例的生命周期并可以保证它们不会比传递的对象寿命长。我用std::string 表示复制对象的成本很高。另一种更安全的解决方案是将shared_ptr 存储在此对象上。但是,这在简单的场景中可能有点过头了。 @nwp 一个让我印象深刻的用例是无复制字符串视图。 我认为您的解决方案实际上非常好。如果你想让它读起来更好一点,你可以使用 std::reference_wrapper 的别名模板。 【参考方案1】:

我会说自然的解决方案是做reference_wrapper 所做的事情:防止临时施工:

struct A 
    const std::string& s;
    A(const std::string& s) : s(s) 
    A(std::string&&) = delete;
;

您还应该记住,默认情况下,具有引用类型的数据成员会使类不可赋值(甚至不能移动赋值),并且通常难以实现赋值运算符。您应该考虑存储指针而不是引用:

struct A 
    const std::string* s;
    A(const std::string& s) : s(&s) 
    A(std::string&&) = delete;
;

【讨论】:

好答案。一个缺点是,如果我们有多个这样的参数,那么delete 所有不需要的口味将非常困难。 我不喜欢指针方法——它没有不能为 nullptr 的语法信息。 @Mikhail 我认为一个不完美但可用的对象优于纯粹但不可用的对象;-) 不过,您可以使用Core Guideline library 中的not_null&lt;T&gt; 之类的东西 是的,这里也想到了not_null【参考方案2】:

Andrzej Krzemieński 在轻量级和方便的"explicit" 库中提供了一个非常简单的类lvalue_ref,它以明确的意图解决了这个问题:

struct Processor

  Big const& _big;
  explicit Processor(lvalue_ref<const Big> b) : _big(b) 
;

const Big b ;
Processor p b; // ok
Processor q Big; // error (temporary)

【讨论】:

以上是关于在类中存储对对象的 const 引用的主要内容,如果未能解决你的问题,请参考以下文章

对象在类中的存储方式有哪些?

使用变量在类中存储自定义函数

将const引用/指针传递给类进行存储的首选方法,而不是复制引用的对象

类成员

在类中存储图像/声音的最佳方式?

static, const