Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storing const reference to an object in class

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?

like image 441
Mikhail Avatar asked Mar 03 '16 11:03

Mikhail


People also ask

Can a const reference be bound to a non-const object?

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 .

Can references be const?

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.

How do you pass a constant reference?

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).

Does const reference copy?

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 .


1 Answers

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;
};
like image 140
Angew is no longer proud of SO Avatar answered Sep 30 '22 18:09

Angew is no longer proud of SO