Why can I modify a const return-by-reference?

  • A+
Category:Languages

Consider this code:

std::unordered_map<std::string, std::string> test; test.insert(std::unordered_map<std::string, std::string>::value_type("test", "123")); std::string& x = test.at("test"); x += "hi"; std::cout << x << ", " << test.at("test") << std::endl; 

This code prints "123hi, 123hi", as expected.

This is counter-intuitive, because unordered_map::at returns a const reference: https://en.cppreference.com/w/cpp/container/unordered_map/at

So, if I make a reference to the return of unordered_map::at, I shouldn't be able to modify its contents, correct? Or have I completely misunderstood the meaning of const returns? The way I understand it, this code shouldn't even compile.

Now, if I change std::string& to std::string, I see the copy constructor executed as expected. The program prints 123hi, 123, showing that the value contained in the map is not modified.

Two questions here:

  1. Why can I modify a const reference?
  2. If I were to use the string x to execute a move operation, such as std::move(x) (referring to the original declaration of string& x) would the move be carried out as expected, or does it ultimately turn into a copy constructor because of the const reference?

 


std::unordered_set::at has two overloads, one for const-qualified and one for non-const-qualified instances.The one for const-qualified instances does indeed return a non-modifiable const reference. Try this:

const std::unordered_map<std::string, std::string> test = {{"test", "123"}}; // ^^ Note the const-specifier  std::string& x = test.at("test"); // No way, compiler will complain 

This example shows that the return value of the const version of std::unordered_set::at can't even bind to a non-const reference, let alone that reference being modified.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: