Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a cast (or standard function) with the opposite effect to `std::move`

Tags:

c++

c++11

First off, this question is not a duplicate of Function dual to std::move? or of Does the inverse of std::move exist?. I am not asking about a mechanism to prevent moving in a situation where it would otherwise take place, and to copy instead; rather I am asking about a mechanism for making a rvalue being accepted in a position that is going to be bound to a modifiable lvalue reference. This is in fact the exact opposite of the situation for which std::move was invented (namely making a modifiable lvalue being accepted in a position that is going to be bound to a (modifiable) rvalue reference).

In the situation that interests me an rvalue will not be accepted, because the context requires a modifiable lvalue reference. For some reason that I don't quite understand but am willing to accept, a (modifiable) rvalue expression will bind to a constant lvalue reference (without introducing an additional temporary), but it won't bind to a modifiable lvalue reference (the error message that gcc is giving me is "invalid initialization of non-const reference of type ‘A&’ from an rvalue of type ‘A’ " while clang says "non-const lvalue reference to type 'A' cannot bind to a temporary of type 'A' "; curiously I cannot get either of these compilers to admit that the expression in question has type 'A&&', even if that expression actually is of the form static_cast<A&&>(...) which by itself raises no error). I can understand that one would not normally want to accept an rvalue expression in a position requiring a modifiable lvalue reference, since it implies that any modifications done via that lvalue reference will be lost, but just as calling std::move is saying to the compiler "I know this is an lvalue that is going to be bound to an rvalue reference (parameter) and therefore might be stolen from, but I know what I am doing and it is OK here" I would like to say in my case "I know this is temporary that is going to be bound to an modifiable lvalue reference (parameter) and therefore any changes that will be made through the lvalue reference will disappear unnoticed, but I know what I am doing and it is OK here".

I can solve the problem by initialising a named object of type A from the rvalue, and then providing the name where a modifiable lvalue reference is needed. I don't think there is any extra runtime overhead for this (a temporary was needed for the rvalue anyway), but having to do this is awkward in several ways: having to introduce a dummy name, maybe having to introduce a compound statement just to hold the declaration, separating the expression producing the rvalue from the function call it is providing an argument for. Whence my question whether this can be done without introducing a dummy name:

  1. Is there any way (for instance using a cast) to bind an rvalue expression of type A to a modifiable lvalue reference of type A& without introducing a named object of type A?
  2. If there is not, is this a deliberate choice? (and if so, why?) If there is, is there a mechanism similar to std::move provided by the standard to facilitate it?

Here is a simplified illustration where I would need such a conversion. I deliberately removed the special constructors of A to be sure the error message do not involve temporaries that the compiler decided to introduce. All errors go away when the A& are replaced by const A&.

class A
{ int n;
public:
  A(int n) : n(n) {}
  A(const A&) = delete; // no copying
  A(const A&&) = delete; // no moving either
  int value() const { return n; }
};

int f(A& x) { return x.value(); }

void g()
{ A& aref0 = A(4); // error
  // exact same error with "= static_cast<A&&>(A(4))" instead of A(4)
  A& aref1 = static_cast<A&>(A(5)); // error
  // exact same error with "= static_cast<A&&>(A(5))" instead of A(5)
  f (A(6)); //error
  // exact same error with "= static_cast<A&&>(A(6))" instead of A(6)

  A a(7);
  f(a); // this works
  A& aref2 = a; // this works too, of course
}

For those who are wondering why I need this, here's one use case. I have a function f with a parameter that serves as input argument, and occasionally also as output argument, replacing the provided value by a "more specialised" value (the value represents a tree structure, and some absent branches might have been filled in); this value is therefore passed as a modifiable lvalue reference. I also have some global variables holding values that sometimes are used to provide a value for this argument; these values are unalterable because they are already completely specialised. In spite of this constant nature, I used to not declare these variables const, since that would make them unsuitable as argument. But they really are assumed to be global and perpetual constants, so I wanted to rewrite my code so as to make this explicit, and also avoid the possibility of accidentally making errors when changing the implementation of f (for instance it might decide to move from its argument when throwing an exception; this would be OK when the argument represents a local variable that is going to be destroyed by the exception anyway, but would be disastrous if it were bound to a global "constant"). I therefore decided to make a copy whenever passing one of these global constants to f. There is a function copy that makes and returns such a copy, and I would like to put a call to it as argument to f; alas copy(c) being an rvalue this cannot be done for the reasons explained above, even though this usage is perfectly safe, and in fact safer than my previous solution.

like image 600
Marc van Leeuwen Avatar asked Jul 21 '14 14:07

Marc van Leeuwen


1 Answers

The most simple solution is this one:

template<typename T>
T& force(T&& t){
   return t;
}
like image 77
gexicide Avatar answered Oct 06 '22 00:10

gexicide