简体   繁体   中英

Mutable Lvalue Reference of Parameter Passed by const Reference

I just found an interesting case: I passed an object by const reference and still I was able to modify its member (which happens to be an lvalue reference ). Following is an example:

#include <iostream>
#include <string>

struct PersonRef
{
  PersonRef(std::string& name_) : _name(name_) {}
  std::string& _name; // <==== IMPORTANT: it is a reference
};

void testRef(const PersonRef& pr) {
  std::string& name = pr._name; // binding to lvalue reference? How does it even compile?
  name = "changed!";
}

int main() {
  std::string name = "trivial_name";
  PersonRef pr{name};

  std::cout << pr._name << "\n"; // prints: trivial_name
  testRef(pr);
  std::cout << pr._name << "\n"; // prints: changed!
}

I used to think that if the parameter is passed by const ref , then object is immutable but this doesn't appear to be the case here. Could someone please explain this? Thanks!

Note that in const member function, the data member _name itself will be considered as const . This doesn't make any difference on _name because it's a reference, which can't be const -qualified. (In a sence the reference is always const , you can't modify the reference itself, the reference can't be rebound to other object after initialization.)

On the other hand, the referenced object won't be become const , so it's still possible to be modified, _name won't become const std::string& (reference to const ).

Similar thing happens on pointer members; in const member function they become const pointers, but not pointers to const ; you still could modify the pointed objects if they're non- const s from the beginning.

Consider a different example.

Let's say you have struct A {int *x;}; .
When accessed through const A &ref , x would have type int *const - a const pointer to (non- const ) int .

As you can see, const is not added recursively to the pointer. It's only added at the top level.


Back to your code. Strictly speaking, const PersonRef& pr is not a const reference. It's a non- const reference to const PersonRef .

(In theory, a const reference would be written as PersonRef &const pr . But since references are not rebindable to begin with, adding const wouldn't do anything, and thus isn't allowed. So technically references are never const , even though you can't rebind them.)

The compiler can't add const -ness to to std::string& _name , since references can't be const . And it doesn't add const -ness recursively to the referenced type, simply because that's how the language works.


If you don't want this behavior, make std::string& _name private and add a pair of const and non- const accesors:

class PersonRef
{
    std::string &_name;
  public:
    PersonRef(std::string& name_) : _name(name_) {}
    std::string &name() {return _name;}
    const std::string &name() const {return _name;}

};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM