3

In error: use of deleted function an error is explained, but it is not explained how to resolve the error. Consider the following c++ code.

struct foo{ int& i; }; int main() { int i = 0, j = 1; foo v = {i}; v = {j}; return 0; } 

This results in error: use of deleted function ‘foo& foo::operator=(foo&&)’ referring to v = {j};. This can be resolved as follows.

struct foo{ int& i; }; int main() { int i = 0, j = 1; foo v = {i}; foo v2 = {j}; return 0; } 

But this is ridiculous. I don't want to declare a new variable, I just want to get rid of the old instance of v and assign it a new value. Is there really no way to simply redefine v?

9
  • The code would initialize the reference i with a reference to a temporary, a disaster. Commented Mar 2 at 9:58
  • You could make int& i into std::reference_wrapper<int> i. Commented Mar 2 at 10:00
  • @Peter-ReinstateMonica Is this not the case in general when you have a reference field in a struct? If this should be described as a disaster, then why is this even a feature? I suppose one possible fix is to use a pointer instead of a reference, but then I can't really use the const keyword to declare that the struct is not allowed to change the object. Commented Mar 2 at 10:01
  • @SmileyCraft My comment was also not correct; the reference to the integer j would stay valid. Commented Mar 2 at 10:06
  • 1
    "I can't really use the const keyword to declare that the struct is not allowed to change the object" - you can have pointer-to-const just like ref-to-const. Commented Mar 2 at 15:31

2 Answers 2

4

Since your struct foo contains a member i which is a reference, it is non-copyable and non-assignable.

In order to make it copyable/assignable, you can use std::reference_wrapper:

std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object.

#include <functional> // for std::reference_wrapper struct foo { std::reference_wrapper<int> i; }; int main() { int i = 0, j = 1; foo v = { i }; v = { j }; return 0; } 

Live demo

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

5 Comments

Even more simply: If you want copy and assignment semantics you can use a pointer right away (which is what the wrapper does behind the scenes anyway, with a lot of fluff that's unnecessary here). The wrapper is only useful for cases in which we'd like to store "naked" references in containers etc. One could say that foo is a minimal reference wrapper, semantically.
@Peter-ReinstateMonica Pointers are not the same as reference wrappers, because they allow nulls, forcing all those who touch them to check for this case that they may not have a good way to handle. You could hide a pointer behind a getter and setter which return/take references to show this non-nullability, but that is pretty much exactly what a std::reference_wrapper does.
@DominikKaszewski I rejected the edit, because AFAIK the important issue here is copyablity (or rather the lack of it due to the reference). It is also non-assignable but this is derived from non-copyability.
@wohlstad There is no copy in code in question, as copyability only concerns constructing new objects as in foo v2 = v;. Here we are trying to overwrite an already existing object, which tries to use assignment.
@DominikKaszewski I editted to add the issue of assignability.
0

The simplest and most adequate solution here is to use a member pointer instead of a reference. The two are very similar — the main difference is that pointers can change what they refer to, which is exactly what you are trying to do with the assignment. References, by contrast, are immutable aliases for objects.

If you want to assign objects of foo, reference members simply are the wrong type choice.

5 Comments

Pointers allow for null state, references don't. You are changing the semantics of the struct in a significant way, which can lead to many subsequent bugs.
@DominikKaszewski As I said elsewhere, the possibility of an invalid state is typically an advantage in my experience. If not, it is trivial to make that guarantee explicit.
Your experience is irrelevant, especially if you do not warn in your answer that you are changing the semantics. You might as well say you should replace every int with std::optional<int> to allow for invalid state. The whole point of a reference is to make it always valid (providing correct lifetime of underlying object).
@DominikKaszewski Sorry, my experience is utterly relevant here ;-).
I think it's fair to say that those tempted to use reference members will tend to be earlier in their C++ journey and would benefit from knowing how this is typically accomplished as opposed to a narrow answer to their question. And while I like reference_wrapper personally, I'd wager pointer is the more common choice. (Yes, you could mention that it is nullable, but that the language in general is more supportive of default-constructible types.)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.