Rationale for const ref binding to a different type?

Viewed 131

I recently learned that it's possible to assign a value to a reference of a different type. Concrete example:

const std::optional<float>& ref0 = 5.0f;
const std::optional<float>& ref1 = get_float();

That's surprising to me. I would certainly expect this to work with a non-reference, but assumed that references only bind to the same type.

I found a pretty good chunk of the c++ standard which talks about all kinds of ways this works: https://eel.is/c++draft/dcl.init.ref#5. But I would appreciate some insight: When is this ever desirable?

A particular occasion where this hurt me recently was this:

auto get_value() -> std::optional<float>{ /* ... */ }
const std::optional<float>& value = get_value();
// check and use value...

I later then changed the return value of the function to a raw float, expecting all uses with a reference type to fail. They did not. Without paying attention, all the useless checking code would have stayed in place.

1 Answers

The basic reason is one of consistency. Since const-reference parameters are very widely used not for reference semantics but merely to avoid copying, one would expect each of

void y(X);
void z(const X&);

to accept anything, rvalue or otherwise, that can be converted to an X. Initializing a local variable has the same semantics.

This syntax also once had a practical value: in C++03, the results of functions (including conversions) were notionally copied:

struct A {A(int);};
struct B {operator A() const;};
void g() {
  A o=B();       // return value copied into o
  const A &r=3;  // refers to (lifetime-extended) temporary
}

There was already permission to elide these copies, and in this sort of trivial case it was common to do so, but the reference guaranteed it.

Related