6

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.

Community
  • 1
  • 1
Marc van Leeuwen
  • 3,605
  • 23
  • 38
  • 3
    Hint: if you make a function taking an rvalue reference, inside that function the argument's name is an lvalue. – R. Martinho Fernandes Jul 21 '14 at 14:41
  • @R.MartinhoFernandes I know that, but this would be making `f` give a completely wrong signal to its callers. Taking an rvalue reference signals "provide a value, and expect it to be stolen from and reduced to useless rubbish". But in fact `f` is usually exporting information through its argument, by specialising its value; only in the rare cases of providing those global constants do I know that no extra information will be coming out. Also taking rvalue reference would oblige me to insert `std::move` in all those normal cases where a local variable serves as argument. – Marc van Leeuwen Jul 21 '14 at 14:48
  • _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_ - [Here's the reason](http://stackoverflow.com/questions/8763398/why-is-it-illegal-to-take-the-address-of-an-rvalue-temporary/9779765#9779765). – ComicSansMS Jul 21 '14 at 14:49
  • For expressions of reference type, the reference is dropped "prior to any further analysis" [expr]/5. Therefore, `static_cast(..)` can be said to have type `A`. The references dropped form the *value category* of the expression. `static_cast(..)` is an xvalue, whereas `A()` is a prvalue. – dyp Jul 21 '14 at 15:01
  • Thank you @dyp for pointing that out; this at least explains the language of the error messages. What is a bit confusing to me is that `static_cast` is used to change the _type_ of an expression, yet by what you cited the types of `x` and `std::move(x)` are exactly the same (once the references are dropped), so the cast in `std::move` did not change the type, but changed the value category instead. But I can live with that confusion. – Marc van Leeuwen Jul 21 '14 at 16:30
  • If such a function was ever to be put forward for standardization, I would propose the name `nobody_move_nobody_gets_hurt`. – Casey Jul 21 '14 at 20:28
  • @Casey So what about poor `auto_ptr` :( – dyp Jul 21 '14 at 21:02
  • 1
    @dyp It should be properly redesigned to only point at automobiles: a language that can compile `auto_ptr` is clearly an abomination. – Casey Jul 21 '14 at 21:07
  • @Casey Well the same problem applies to `auto my_car = boat{};` I guess in C++2x we'll see a recycled `auto_ptr` that can only point to objects of automatic storage duration. – dyp Jul 21 '14 at 21:17
  • Your indentation style is horrid. – Lightness Races in Orbit Aug 15 '14 at 17:17
  • Technically had a use for such function in generic code where I needed to call for `std::begin ()` to act exactly as `.begin()` member function of function result type. I solved it by storing result of the function in `auto&&` but it's probably not very beautiful solution. – Predelnik Oct 25 '18 at 17:45

4 Answers4

9

The most simple solution is this one:

template<typename T>
T& force(T&& t){
   return t;
}
gexicide
  • 38,535
  • 21
  • 92
  • 152
7

The function provided below is a bad idea. Don't use it. It provides a very easy path towards dangling references. I'd consider the code that needs it faulty and act accordingly.

Still, I think this is an interesting exercise for some reason, so I can't help but show it.

Inside a function, the names of its arguments are lvalues, even if the arguments are rvalue references. You can use that property to return an argument as an lvalue reference.

template <typename T>
constexpr T& as_lvalue(T&& t) {
    return t;
};
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • Your function takes a universal references, it is not restricted to rvalue references. That's even better anyway... BTW: the `std::remove_reference` is superfluous. – Deduplicator Jul 21 '14 at 14:56
  • @Deduplicator true, I simply stole the signature from `std::move` :) – R. Martinho Fernandes Jul 21 '14 at 15:06
  • Like for the answer by Deduplicator, I don't see why you should be so dismissive about potential use of this function template. Like `std::move` it is designed for specific uses, and can have disastrous effects when randomly used independently of that use case. I've described at the end of my question a concrete situation where its use seems to be justified; is there anything you have against this particular use? – Marc van Leeuwen Jul 21 '14 at 16:08
  • In fact I would accept this answer if you left out the first paragraph. The name `as_lvalue` is certainly the best choice, and it is interesting to see that a function (template) can do what a cast cannot. – Marc van Leeuwen Jul 21 '14 at 19:19
2

This appears to work in all of your cases:

template <class T>
constexpr typename std::remove_reference<T>::type& copy(T&& t) {
    return t;
};

It's exactly like std::move, except it returns an lvalue reference instead.

Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324
  • 2
    Why bother with the `remove_reference` jumbo? – Yakk - Adam Nevraumont Jul 21 '14 at 15:27
  • Because otherwise with T& t [in] a T& return type template instance would be called. – Solkar Jul 21 '14 at 17:45
  • I don't know enough about template deduction logic to see why this solution would be superior to the one proposed by gexicide, but the `constexpr` and the name `force_copy` you chose seem to indicate you did not understand the intended use. This is not about forcing a copy; the copy will be made by _explicitly_ invoking a function (and the resulting address will certainly not be a compile time constant), but the question is to make the result be accepted where a `T&` is to be bound. – Marc van Leeuwen Jul 21 '14 at 19:16
  • @MarcvanLeeuwen I've called it `force_copy` so it's something like `std::move`. I would have called it `copy`, but that would easily have been confused with `std::copy`. In the same way that `std::move` doesn't actually move an object, this `copy` doesn't actually copy. And I have made it `constexpr` in the same way that `std::move` is `constexpr` (in C++14). – Joseph Mansfield Jul 21 '14 at 19:41
  • *"but that would easily have been confused with `std::copy`"* You mean, like `std::move` (the cast) and `std::move` (the algorithm)? ;) – dyp Jul 21 '14 at 21:00
  • @dyp I completely forgot about the algorithm. Honestly, that's just bad naming in the standard library. But yeah, for consistency, I'll also change mine to just `copy`. – Joseph Mansfield Jul 21 '14 at 21:02
1

Leave perfect forwarding out of your forwarding function, and you have no_move:

template<class T> constexpr T& no_move(T&& x) {return x;}

Just be sure pretending you have an lvalue-reference instead of an rvalue reference is ok, as that circumvents the protection against binding of temporaries to non-const references and such.

Any code using this function is with near-certainty defective-by-design.

In your example use-case, the proper way would be changing the argument-type of f() to const& so no such adaptor is neccessary, and using const_cast to add to the cache of saved computations.
Be sure not to change non-mutable members on an object declared const though.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • 1
    Are you implying that the use in the function `f` I described near the end of my question is defective by design? If so, what is the defect, and how should the design be altered? – Marc van Leeuwen Jul 21 '14 at 16:00
  • `f` should get its argument by `const&`, thus no adaptor neccessary. So, too restrictive prototype is the "defect" in that case. Fairly harmless, as long as you can change it. – Deduplicator Jul 21 '14 at 16:05
  • 1
    Did you read the question to the end? Part of the functionality of `f` is potentially exporting information by modifying its argument; this is not possible if the argument is `const&`. (And the exporting case is easily proved to never happen in case the argument is a copy of one of those "global constants"). – Marc van Leeuwen Jul 21 '14 at 16:11
  • @MarcvanLeeuwen: Looks like a logically constant object (that's what `const` means), with internal caching. That's a case for `const_cast` in addition to the argument type change I mentioned (be sure not to actually modify a variable declared `const` without `mutable` members though). Sorry for not including that bit before... – Deduplicator Jul 21 '14 at 16:17
  • 1
    I think you rather misunderstood my example; the explanation you added makes things worse. The argument of `f` _must be modifiable_, this is part of the specification of `f`. Think of `f` as a function mapping a template (like class templates in C++, but in a different language that my program is compiling) to an instance of that template; this is the "adding branches" part. It is just those very special global values that won't ever change, because that are already fully specialised (no template arguments) types. There's no cache or mutable parts involved. – Marc van Leeuwen Jul 21 '14 at 16:39
  • So, actually the signature is more like `const instantiation& f(const my_template&)`, and `f` checks for a pre-existing instantiation and otherwise adds one for/to the template? – Deduplicator Jul 21 '14 at 16:48
  • 1
    No, the possible changes are exported _via the argument you called `my_template`_, not as return value (there is a different and not directly related return value). Please take my word for it that the argument to `f` _must be modifiable_ (I think I said this before at some point). There is no point in insisting that I make it `const`; it won't compile if I do. Arguments that serve both for input and for output have their uses, and this is one of them. What is slightly unusual is only that for _some_ inputs, the value predictably won't be modified (but it still provides necessary information). – Marc van Leeuwen Jul 21 '14 at 19:01
  • How and why are they exported via the input argument? Well, I'll take your word for it being useful/neccessary and that it really cannot be compared to lazy-evaluation or caching. In that case, consider adding a gatekeeper-function-overload doing that transformation and (at least in debug mode) verifying it is safe. Heavily document that though, because next time you look, it will still look suspicious. – Deduplicator Jul 21 '14 at 19:13