Reputation: 6992
There are cases when I want a reference to an object but instead I get a copy. Here is an example:
std::pair<const std::string, int> foo("hello", 5);
const std::pair<std::string, int> & bar = foo;
std::cout << "foo: " << foo.first << " " << foo.second << std::endl;
std::cout << "bar: " << bar.first << " " << bar.second << std::endl;
foo.second = 7;
std::cout << "foo: " << foo.first << " " << foo.second << std::endl;
std::cout << "bar: " << bar.first << " " << bar.second << std::endl;
This produces:
foo: hello 5
bar: hello 5
foo: hello 7
bar: hello 5
So apparently a copy of foo
has been created while the syntax suggests (to me at least) that the programmer wanted a reference to it.
This violates the principle that a reference should be an alias to something. It would be great if somebody could explain what is going on and why.
(Note: I came across this here)
Upvotes: 13
Views: 718
Reputation: 227538
The underlying types of foo
and bar
are different, so a temporary is created using an implicit conversion from the type on the RHS to the one on the LHS*. The C++ standard allows for a const
reference to bind to a temporary and extend its lifetime.
The const
reference bar
binds to that temporary, which is a distinct object from foo
.
If you were to use the same types, you'd get the result you expect:
std::pair<const std::string, int> foo("hello", 5);
const std::pair<const std::string, int> & bar = foo;
or
std::pair<std::string, int> foo("hello", 5);
const std::pair<std::string, int> & bar = foo;
would yield
foo: hello 5
bar: hello 5
foo: hello 7
bar: hello 7
*std::pair
has a template constructor that allows this implicit conversion from one type of pair to another.
Upvotes: 15
Reputation: 171167
That's a special property of references to const
(and of rvalue refereneces, naturally). These references can bind to temporary objects.
Notice that std::pair<const std::string, int>
(the type of foo
) is a different type than std::pair<std::string, int>
(the type to which bar
wants to refer, modulo const
). There is no object of type std::pair<std::string, int>
in your code, so bar
cannot bind to any such object.
However, as I said, references to const
and rvalue references can bind to temporaries. And an object of type std::pair<std::string, int>
can be implicitly created from an object of type std::pair<const std::string, int>
. Therefore, such a temporary object is created, and bar
is bound to that temporary. This reference binding also extends the lifetime of the temporary to that of bar
*.
That's why you get a copy. If you changed the type of bar
to std::pair<std::string, int> &
(i.e. dropped the const
), you'd instead get a compilation error that a non-const lvalue reference cannot bind to a temporary.
* In the special case of a member variable of reference type bound to a temporary, the temporary's lifetime will only extend until the end of the constructor which initialised the reference.
Upvotes: 5