这听起来像是一个基础问题,但我没有找到任何全面的答案,所以在这里给出。考虑以下代码片段:
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;
}
演示。
据我所知,这是未定义行为(UB)。字符串字面量“abc”在构造函数中绑定到了const std::string&
,创建了一个临时字符串对象。它还绑定到了引用a.s
,并在a
被构造后立即被销毁。也就是说,常量引用不能链式延长生命周期。悬空引用,崩溃。在这种特殊情况下,在 ideone.com 上我看不到任何输出,但是任何事情都可能发生(记得迅猛龙吗)。
好的,这一点很清楚。但是如果这实际上正是我们的目的:我们想要存储一个常量引用到一个对象?到一个现有的对象,而不是临时对象?这听起来像是一个非常自然的任务,但我只想到了一种(几乎)自然的解决方案。通过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
拒绝除 const l-value 字符串之外的所有内容。我认为这甚至更不优雅。
还有其他方法吗?
更新 另一个问题:这是否是 std::reference_wrapper
的合法用法,还是可能被认为过于特定?
A
为什么不能只有一个普通的std::string
?即使A::s
接收到左值并且引用打破了赋值运算符,它也很可能会悬空。这是否有用例或纯粹是学术性的? - nwpA(const std::string &&) = delete;
可能会实现你想要的效果,但我认为这并不是一个好主意。 - nwpstd::string
表示昂贵的可复制对象。另一种更安全的解决方案是在此对象上存储shared_ptr
。但是,在简单情况下,这可能过于繁琐。 - Mikhail