You can't rebind a top level reference just like you can't change a top level const. But when they exist in classes you can as of c++20.
With c++20 you can rebind references but with significant gotchas. They, rvalue and lvalue refs, delete the defaulted assignment operator so you have to write custom member functions to make such a class generally usable.
Rebinding lvalue refs simply binds it to a different lvalue. rvalue refs bind to a tmp with no name and hence have special characteristics affecting the lifetime of the tmp objects they reference. Their lifetime ends when the scope of the original rvalue refs ends. While, if they are in a class, they can be rebound to a different rvalue the scope of that class object doesn't matter. Rather it's the scope of the initial class object that ends its lifetime.
You can not rebind a top level reference but, as of c++20 you can rebind one that exists in a class using std::construct_at
.
Example lvalue ref:
#include <memory>
struct A { int& i; };
inline int f2(int i) {return 2 * i;}
int main() {
float x = 3.14f;
float&& expr = x * x + x * x; // expr binds to the result of an expression
expr = x; // this isn't a rebind, just an assignment to an rvalue reference
float& y = x;
// no way to rebind y. It's an lvalue reference
// and it cannot be changed to refer to another lvalue.
// However, lvalue references that are inside a class can
// be changed since an object of the class can be identified
int i1{ 1 };
int i2{ 2 };
// lvalue reference in a class
A a0{ i1 };
A a1{ i2 };
std::construct_at(&a0, a1); // a now contains a ref to i2;
Here's a program that demonstrates how rebinding an rvalue ref doesn't extend the ref's life even though the object it's rebound to has a longer life.
#include <memory>
#include <iostream>
struct B {
inline static int seq{};
int i;
B() :i(seq++) {}
~B() {std::cout << "dtor " << i << "\n";}
};
B f(){return B();}
struct A {
B&& rref;
};
int main() {
{
A a0{ f() };
std::cout << "a0.rref.i=" << a0.rref.i << "\n";
{
A a1{ f() };
std::construct_at(&a0, std::move(a1)); // a0.rref's tmp now disconnected replaced by a1.rref
std::cout << "a0.rref.i=" << a0.rref.i << "\n";
std::cout << "a1.rref.i=" << a1.rref.i << "\n";
} // the tmp object referred to by both a0.rref and a1.rref, destroyed here when a1 goes out of scope
} // the original tmp, which a0.rref n longer aliases, is destroyed here when a0 coes out of scope
}
/* Outputs *
a0.rref.i=0
a0.rref.i=1
a1.rref.i=1
dtor 1
dtor 0
***********/
Thanks to @HolyBlackCat for setting me straight that rvalue refs can, indeed be rebound. They can even refer to the same tmp object. But watch the lifetimes.
On another note, unlike, lvalue refs, I can't think of a use case for rebinding rvalue refs in a class. And only rarely for lvalue refs in a class.