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?
No. A reference is simply an alias for an existing object. const is enforced by the compiler; it simply checks that you don't attempt to modify the object through the reference r .
The grammar doesn't allow you to declare a “const reference” because a reference is inherently const . Once you bind a reference to refer to an object, you cannot bind it to refer to a different object.
When you pass by const reference, you take the argument in by reference (avoiding making any copies of it), but cannot make any changes to the original object (much as would happen when you would take the parameters in by value).
Not just a copy; it is also a const copy. So you cannot modify it, invoke any non-const members from it, or pass it as a non-const parameter to any function. If you want a modifiable copy, lose the const decl on protos .
I'd say the natural solution would be to do what reference_wrapper
does: prevent construction from temporaries:
struct A {
const std::string& s;
A(const std::string& s) : s(s) {}
A(std::string&&) = delete;
};
You should also bear in mind that having a data member of reference type makes the class non-assignable (not even move assignment is possible) by default, and it's generally difficult to implement an assignment operator. You should consider storing a pointer instead of a reference:
struct A {
const std::string* s;
A(const std::string& s) : s(&s) {}
A(std::string&&) = delete;
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With