17

Recently, I have followed a discussion about assignments to expressions in C++ as shown in the following example:

string s1, s2, s3;
(s1 + s2) = s3;

With C++11 it is possible to restrict the assignment operator to lvalue references (on the left side). When declaring the assignment operators as follow, the compiler Clang rejects the code with an error message due to incompatible types.

auto operator=(const string& rhs) & -> string&;
auto operator=(string&& rhs) & -> string&;

I haven't seen this anywhere. Is there a good reason for not using lvalue reference qualifiers for assignment operators (besides missing support in most compilers)?

nosid
  • 48,932
  • 13
  • 112
  • 139
  • 5
    One reason is that most compilers don't support the syntax yet. Another is that it doesn't solve a *major* problem. How often does this happen by mistake? – Bo Persson Oct 06 '12 at 11:31
  • 1
    I think it does happen. `if (somefunc() = value)` Of course, most compilers emit a warning for this, but not in all cases. – Tamás Szelei Feb 26 '14 at 14:23

3 Answers3

8

Interesting! I wasn't even aware of this and took me while to find it (it was part of the "Extending move semantics to *this" proposal). The notation is defined in 8.3.5 [dcl.decl] paragraph 4 in case anybody wants to have a look.

Anyway: Now, knowing about this feature it seems it is most useful to use it for overloading and possibly behave differently if the object on which a function is called is an lvalue or an rvalue. Using it to restrict what can be done, e.g., with the result of an assignment seems unnecessary, especially if the object actually happens to be an lvalue. For example, you might want the syntax to return an rvalue from assigning to an rvalue:

struct T {
    auto operator=(T&) & -> T&;
    auto operator=(T&&) & -> T&;
    auto operator=(T&) && -> T;
    auto operator=(T&&) && -> T;
};

The intention here would be to enable moving from the assignment result (whether that is worth it, though, I'm not sure: why not skip the assignment in the first place?). I don't think that I would use this feature primarily to restrict uses.

Personally, I like the possibility to sometimes get hold of an lvalue from an rvalue and the assignment operator is often a way to do this. For example, if you need to pass an lvalue to a function but you know you don't want to use anything with it, you can use the assignment operator get hold of an lvalue:

#include <vector>
void f(std::vector<int>&);
int main()
{
    f(std::vector<int>() = std::vector<int>(10));
}

This may be an abuse of the assignment operator to get an lvalue from an rvalue but it is unlikely to happen by accident. Thus, I wouldn't go out of my way and make this impossible by restricting the assignment operator to be applicable to lvalues only. Of course, returning an rvalue from an assignment to an rvalue would also prevent this. Which of the two uses is more useful, if any, might be a consideration.

BTW, clang seems to support the syntax you quoted since version 2.9.

Morwenn
  • 21,684
  • 12
  • 93
  • 152
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • 6
    I'd rather have a clear `template T& as_lvalue(T&& v){ return v; }` function than screwing with the minds of my fellow coders that wonder just what the heck you're trying to achieve with the assignment (which, for the move-unaware, also seems to include a useless copy of the `vector`). – Xeo Oct 06 '12 at 13:23
4

Is there a good reason for not using lvalue reference qualifiers for assignment operators (besides missing support in most compilers)?

No, not really. Using lvalue or rvalue qualifiers to construct a correct interface for lvalue or rvalue objects is just the same as using const, and it should be approached the same way- each function should be considered for restriction. Assignment to an rvalue doesn't really make sense, so it should be forbidden.

The reason you haven't seen it is mostly poor compiler support- rvalue refs for *this is kinda like thread_local, most compiler implementers seem to have put it near the bottom of the "Features to implement from C++11" stack.

SSJ_GZ
  • 781
  • 1
  • 6
  • 17
Puppy
  • 144,682
  • 38
  • 256
  • 465
  • In my view the best answer. Although I was more interested on the assignment operator, because it is a special member function. Using reference qualifiers might have some effect on the rule-of-5, inheritance, being a member variable of an aggregate or usage with standard containers and the standard library. However, I should have been asking for this information. – nosid Oct 07 '12 at 13:21
  • 2
    Be careful reading this answer. If you only read the title of the question, you might think the 'No, not really' answers the question title. It answers the question at the end of the question 'Is there a good reason for not using lvalue reference qualifiers for assignment operators?' – Miles Rout Nov 22 '19 at 02:16
  • Also note that the compiler support mentioned in this answer has improved considerably since it was written. Every major and almost every minor compiler supports ref-qualifiers as of 2019. – Miles Rout Nov 22 '19 at 02:20
4

One reason I'm not super enthusiast about your suggestion is that I'm trying to shy away from declaring special members altogether. Most of my assignment operators are thus implicitly declared and thus have no ref-qualifiers.

Of course, for those times when I do write a class or class template to e.g. manage ownership (see conclusion in above link), I could take care to declare those operators for lvalues only. Since it has no effects on clients however, there isn't much of a point.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • I agree with you. If possible I avoid declaring any of the 5. However, the compiler generated assignment operator is not restricted to lvalue references. Most likely to be backward compatible. – nosid Oct 07 '12 at 13:24