6

I was wondering, is there a reason the assignment operator of standard types is not lvalue ref-qualified? None of them are.

Because of that, we can write things such as this:

std::string{} = "42";
std::string s = "hello " + std::string{"world"} = "oops!";

std::vector<int> v = { 1,2,3 };
std::move(v) = { 4,5,6 };

If the assignment operator was lvalue ref-qualified all of these examples would not compile.

Is it because there's a lot of things to modify (but then so it was for noexcept) and nobody wrote a proposal for? I don't think people write code like this but shouldn't the library be designed so that it doesn't even allow it?

2 Answers 2

6

Your suggestion was proposed in 2009, and ultimately rejected in Frankfurt that year over "concerns about backwards compatibility".

It would have been a breaking change, and we don't like those.

The existing prohibition against assigning to rvalues of built-in types is only of limited real value anyway, so the cost of potentially breaking existing code was almost certainly deemed to be "not worth it".

Would the library be designed in this manner if we had a clean slate? Perhaps.

Sign up to request clarification or add additional context in comments.

3 Comments

Bah, just depricate it, wait 6 years, and kill it. 99/100 uses are going to be bugs.
I see. But what would it break? Anything more than some niche cases?
@MariusBancila No that's pretty much it, and Adam's right. But still. Seems to me a perfect candidate for a "what were we thinking this was not worth the unexpected catastrophe that has ensued" situation a few years down the line. Don't fix what isn't completely and utterly broken, I guess.
1

There's been some progress on this question in C++23. C++11 codified in the library the pre-existing best practice that "const means thread-safe." C++23 finally codifies in the library the pre-existing best practice that "const means callable on rvalues" — specifically, "const-assignable means proxy reference."

A type such as tuple<int, int> continues to have the ordinary tuple& operator=(const tuple&). But a type such as tuple<int&, int&> — the return value of std::tie — now has const tuple& operator=(const tuple&) const, too. So that this works in C++23:

int i=1, j=2;
const auto tup = std::tie(i, j);  // Note `const` here!
tup = std::make_tuple(j, i);      // Proxy references are const-assignable

This means that we can finally distinguish types that are supposed to be rvalue-assignable (i.e., proxy-reference types like tuple<int&>) from types that are not supposed to be rvalue-assignable (i.e., all other types).

And that means that we can finally give a compiler warning whenever we see someone trying to assign to a non-const rvalue of a non-proxy-reference class type! I've implemented the diagnostic in my personal fork of Clang; it just needs someone (not me) to volunteer to upstream it into Clang trunk and/or GCC.

Here is your example (Godbolt), showing the new diagnostic:

<source>:5:17: warning: possibly unintended assignment to an rvalue of
type 'std::string' (aka 'basic_string<char>') [-Wassign-to-class-rvalue]
    5 |   std::string{} = "42";
      |   ~~~~~~~~~~~~~ ^
.../include/c++/v1/string:1270:69: note: declared here
 1270 |   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& operator=(const value_type* __s) {
      |                                                                     ^

In other words, we can get your desired compiler behavior today without changing the language specification at all. The compiler can detect this kind of bug, for people who want to detect it (should be almost everyone); without actually breaking old code for people with billion-line codebases, and without complicating the language spec with special cases for assignment.

The blog post goes into more detail about the complicated corner cases. For example, it's not just operator= that you'd care about, but also operator+= and postfix operator++... and what about prefix operator++, should we add a ref-qualifier to it too?... and so on. Moving the feature into a simple compiler diagnostic moots all those thorny questions; compiler vendors can iterate on the quality of their diagnostic without interacting with the language-standardization process.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.