Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move construction from const reference

I have the following situation where I need to move construct t2 from t1. Unfortunately it is not possible to do that (constness violation I suppose)
What is the right approach to handle that transparently from the caller of foo ? (ie. without requiring a passing by value and an explicit std::move)

struct T
{
    T() = default;
    ~T() = default;
    T(T&&) = default;
};

T foo(const T& t) 
{
    T t3;

    if (predicate)
        return t3;
    else
        return std::move(t);
}

int main()
{
  T t1;
  T t2 = foo(t1);

  return 0; 
}
like image 410
3XX0 Avatar asked May 25 '13 00:05

3XX0


People also ask

Can you move a const value?

First of all, you cannot move from an object that is marked as const . Another potential problem is when a compiler is trying to use (Named) Return Value Optimization (NRVO or RVO).

How do I turn off move constructor?

To correct this, remove the move constructor completely. In the case of the class, once a copy constructor is present (user defined), the move is implicitly not generated anyway (move constructor and move assignment operator).

Are move constructor automatically generated?

If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then: No move constructor is automatically generated. No move-assignment operator is automatically generated.


2 Answers

What is the right approach to handle that transparently from the caller of foo ?

You cannot do that, and that's intended to be so. An lvalue is never moved from transparently.

Since a moved-from object is usually left in an unknown (yet legal) state, it would be dangerous if a client could pass an lvalue as the argument to a function, and that function were allowed to silently move from it. The client may be left with a zombie object without even knowing it!

Therefore, the language enforces the rule that moving from an lvalue must be a conscious action, and shall occur by explicitly turning the lvalue into an rvalue (actually, an xvalue) through a call to std::move().

In this case, I would say your function foo() - whatever it is actually supposed to do - should take an rvalue reference, and the client should invoke it like so:

T t2 = foo(std::move(t1)); 

Notice, that this last suggestion may not be correct once you turn a meaningless name such as foo() into something that reflects the semantics of some particular operation in some concrete program. Without knowing what is the meaning of that operation, however, I can only provide formal, mechanical advice on how to make your code snippet compile, and how to think about move semantics in general.

like image 158
Andy Prowl Avatar answered Oct 04 '22 22:10

Andy Prowl


You could do this if you wanted to badly enough. To do it, your foo would end up as essentially a re-implementation of std::move.

struct T
{
    T() = default;
    ~T() = default;
    T(T&&) = default;
};

template<class T> struct my_remove_reference      {typedef T type;};
template<class T> struct my_remove_reference<T&>  {typedef T type;};

// The third part of a normal `remove_reference`, but which we don't need in
// this case (though it'll all still be fine if you un-comment this).
//template<class T> struct my_remove_reference<T&&> {typedef T type;};

template <typename T>
typename my_remove_reference<T>::type &&foo(T &&t) {
    return static_cast<typename my_remove_reference<T>::type &&>(t);
}

int main()
{
  T t1;
  T t2 = foo(t1);

  return 0; 
}

Now, despite the fact that you can/could do this, the other answers remain more or less correct -- even though you can do it, you almost certainly shouldn't. If you used this foo on a real class that supports move construction (i.e., will really move the state from the source to the destination) you'd end up basically destroying your source object even though it remains visible.

Doing this with your edited question, where foo could, but wouldn't always, destroy the source object would be particularly pernicious. With the right name that makes it clear that foo really moves from the source, it could, possibly, be just barely reasonable to do this -- but (at least to me) a function that might or might not destroy its argument seems drastically worse than one that does so dependably. Obviously, a version of foo like above that just does the same thing as std::move is pointless, but a version that also did some other processing on the input (and, for whatever reason, couldn't be used along with std::move) might, possibly, be just barely acceptable if you really didn't have any other choice.

like image 37
Jerry Coffin Avatar answered Oct 04 '22 20:10

Jerry Coffin