这听起来像是一个基本问题,但我没有找到任何全面的答案,所以在这里。请考虑以下代码段:
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
就会被销毁。也就是说,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
拒绝除const l-value字符串之外的所有内容。我认为这更不优雅。
还有其他方法吗?
UPD 另一个问题:这是std::reference_wrapper
的合法用法,还是可能被认为过于具体?
答案 0 :(得分:4)
我说自然解决方案是做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;
};
答案 1 :(得分:0)
AndrzejKrzemieński在轻巧方便的lvalue_ref
库中提供了一个非常简单的类"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)