Search code examples
c++c++11referenceplacement-new

Is it possible to reset reference to another value in C++?


I know generally it's impossible to reset a reference after it's already initialized.

However, I somehow try out the following code and it happens to work on both clang++ and g++.

My question is, is the following a valid (behavior-defined) C++?

std::string x = "x";
std::string y = "y";
std::string i = "i";
std::string j = "j";

// now references to x, y
std::pair<std::string &, std::string &> p { x, y };
p.first = "1"; //changes x
p.second = "2"; //changes y
// now references to i, j
new (&p) std::pair<std::string &, std::string &> {i, j};
p.first = "1"; //changes i
p.second = "2"; //changes j

The above code works on g++ and clang++, but is it good C++? Thanks.


Solution

  • The snippet has undefined behaviour, but barely so. Conceptually, you destroyed the old references and created new ones, you didn't rebind the reference even if you reused the memory. This part is completely fine.

    The catch is if the reused class contains const or reference members, then the original name of the variable cannot be used to refer to the new object

    new (&p) std::pair<std::string &, std::string &> {i, j};
    // p does not refer to the newly constructed object
    p.first = "1";  // UB
    p.second = "2"; // UB
    

    The fix is simple, in this case

    auto p2 = new (&p) std::pair<std::string&, std::string&> {i, j};
    p2->first = "1";
    p2->second = "2";
    

    Another solution is the C++17 function std::launder

    new (&p) std::pair<std::string &, std::string &> {i, j};
    std::launder(&p)->first = "1";
    std::launder(&p)->second = "2";
    

    These rules presumably enables the compiler to make more optimizations around references and const members.