This sounds like a basic question, but I didn't find any comprehensive answer, so here it is. Consider this code snippet:
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.
As long as I understand, this is UB. String literal "abc" binds to const std::string&
in constructor, creating a temporary string object. It is also bound to reference a.s
, and it is destroyed once a
is constructed. That is, const reference cannot chain lifetime prolongation. Dangling reference, boom. In this particular case I see no output at all on ideone.com, but anything could happen (remember velociraptors).
Ok, this one is clear. But what if this is actually our very intent: we want to store a const reference to an object? To an existing one, not to temporary? This sounds like a very natural task, yet I came up with only one (almost) natural solution to it. Accepting constructor's argument by std::reference_wrapper
instead of by reference:
A(std::reference_wrapper<const std::string> r) : s(r) {}
Since std::reference_wrapper
has deleted constructors from temporaries:
reference_wrapper( T&& x ) = delete;
this works just like expected. However, this is not quite elegant. Another approach I can think of is to accept forwarding reference T&&
and to reject everything except const l-value strings with std::enable_if
. This is even less elegant, I think.
Any other approaches?
UPD Another question: is this a legitimate usage of std::reference_wrapper
, or may it be considered too specific?