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

Storing const reference to an object in class

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

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&,创建一个临时字符串对象。它也绑定到引用 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&& 并拒绝除具有 std::enable_if 的常量左值字符串之外的所有内容。我认为这更不优雅。

还有其他方法吗?

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

我想说自然的解决方案是做 reference_wrapper 所做的事情:防止临时构造:

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

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

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

Andrzej Krzemieński 有一个非常简单的 class lvalue_ref provided in the lightweight and convenient "explicit" 库,它明确地解决了这个问题:

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)