0

An example here for std::forward,

// forward example
#include <utility>      // std::forward
#include <iostream>     // std::cout

// function with lvalue and rvalue reference overloads:
void overloaded (const int& x) {std::cout << "[lvalue]";}
void overloaded (int&& x) {std::cout << "[rvalue]";}

// function template taking rvalue reference to deduced type:
template <class T> void fn (T&& x) {
  overloaded (x);                   // always an lvalue
  overloaded (std::forward<T>(x));  // rvalue if argument is rvalue
}

int main () {
  int a;

  std::cout << "calling fn with lvalue: ";
  fn (a);
  std::cout << '\n';

  std::cout << "calling fn with rvalue: ";
  fn (0);
  std::cout << '\n';

  return 0;
}

Output:
calling fn with lvalue: [lvalue][lvalue]
calling fn with rvalue: [lvalue][rvalue]

mentions that

the fact that all named values (such as function parameters) always evaluate as lvalues (even those declared as rvalue references)

Whereas, the typical move constructor looks like

ClassName(ClassName&& other)
   : _data(other._data)
{
}

which looks like _data(other._data) should invoke the move constructor of _data's class. But, how is it possible without using std::forward? In other words, shouldn't it be

ClassName(ClassName&& other)
   : _data(std::forward(other._data))
{
}

?

Because, as pointed out in std:forward case,

all then named values should evaluate as lvalue

I more and more like C++ because of the depth of issue like this and the fact that the language is bold enough to provide such features :) Thank you!

Chris Beck
  • 15,614
  • 4
  • 51
  • 87
user557583
  • 33
  • 5
  • 2
    You should be using `std::move` in the constructor. see: [How to define a move constructor?](http://stackoverflow.com/questions/9456910/howto-define-a-move-constructor) – NathanOliver Feb 12 '16 at 20:42
  • 2
    _"Whereas, the typical move constructor looks like ..."_ No it doesn't. Also, `std::forward` is for _forwarding_ things as their original value category, either an rvalue or lvalue, depending on the template argument used with `std::forward`. Just saying `std::forward(other._data)` without a template argument list won't even compile . If you just want an rvalue, use `std::move` not `std::forward`. – Jonathan Wakely Feb 12 '16 at 22:13
  • @JonathanWakely But if the argument is lvalue ClassName(ClassName&& other) would not even be called because the function prototype is not matched. – user557583 Feb 13 '16 at 00:47
  • @user557583, it might start as an rvalue, but once you're inside the constructor it has a name and is an lvalue. Read how forwarding references work. That's not how you use `std::forward`, and it's not how you write a move constructor. – Jonathan Wakely Feb 13 '16 at 12:49

3 Answers3

3

A typical move constructor looks like this (assuming it is explicitly implemented: you might want to prefer = default):

ClassName::ClassName(ClassName&& other)
    : _data(std::move(other._data)) {
}

Without the std::move() the member is copied: since it has a name other is an lvalue. The object the reference is bound to is an rvalue or an object considered as such, however.

std::forward<T>(obj) is always used with an explicit template argument. In practice the type is that deduced for a forwarding reference. These look remarkably like rvalue references but are something entirely different! In particular, a forwarding reference may refer to an lvalue.

You may be interested in my Two Daemons article which describes the difference in detail.

Chris Beck
  • 15,614
  • 4
  • 51
  • 87
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • I see. Hmm actually I took the (wrong?) move constructor snippet from msdn page: https://msdn.microsoft.com/en-us/library/dd293665.aspx. Did MS make a mistake then? – user557583 Feb 13 '16 at 00:43
  • @user557583: the page doesn't seem to have any move constructor showing how to move a member which itself has a move constructor. It does show ill-advised implementations of the copy assignment operator (it isn't exception-safe as the allocation may throw resulting in a double-delete) and move constructor (there is no point to first initialize the members in the initializer list and then set them immediately in the body). The "Robust Programming" is also ill-advised as it creates mandated overhead (a self-assignment check and unnecessary initialization and delete). That's a pretty bad page! – Dietmar Kühl Feb 13 '16 at 12:13
  • Kuhl I see. I am just surprised that Microsoft's official coding guide is misleading. – user557583 Feb 14 '16 at 08:23
  • @user557583: note that the move constructor is correct (although written in a silly way). It doesn't show a case, however, it doesn't even do what you asked about (construct a member from the move constructor's coresponding member). Even if it did, it would be OK because the member happens to be a pointer and built-in types don't have special move behavior anyway: even when `std::move()`d it would just be a copy. On what the want to show the page isn't wrong. – Dietmar Kühl Feb 14 '16 at 08:36
  • Right, The use of move constructor to just to move the pointer value is not very meaningful example to demonstrate the advantage of move constructor. – user557583 Feb 16 '16 at 01:08
  • @user557583: depends on the view: in theory it should only be necessary to actually implement a move constructor for classes actively managing a resource. These are unlikely to have members of class type. For all others the implicit or, at least, the defaulted move constructor should do. If they don't you are likely in a somewhat nire conplex situatiin anyway. At least, that's the expectation. – Dietmar Kühl Feb 16 '16 at 07:54
1

This Ideone example should make things pretty clear for you. If not, keep reading.

The following constructor accepts Rvalues only. However, since the argument "other" got a name it lost its "rvalueness" and now is a Lvalue. To cast it back to Rvalue, you have to use std::move. There's no reason to use std::forward here because this constructor does not accept Lvalues. If you try to call it with a Lvalue, you will get compile error.

ClassName(ClassName&& other)
     : _data(std::move(other._data))
{
    // If you don't use move, you could have: 
    //    cout << other._data;
    // And you will notice "other" has not been moved.    
}

The following constructor accepts both Lvalues and Rvalues. Scott Meyers called it "Universal Rerefences", but now it's called "Forwarding References". That's why, here, it's a must to use std::forward so that if other was an Rvalue, _data constructor will get called with an Rvalue. If other was an Lvalue, _data will be constructed with an Lvalue. That's why it's called perfect-forwarding.

template<typename T>
ClassName(T&& other)
   : _data(std::forward<decltype(_data)>(other._data))
{
}

I've tried to use your constructors as an example so you could understand, but this is not specific to constructors. This applies to functions as well.

With the first example tho, since your first constructor only accepts Rvalues, you could perfectly use std::forward instead, and both would do the same thing. But it's best not to do it, because people may think that your constructor accepts a forwarding reference, when it actually doesn't.

ashvardanian
  • 424
  • 1
  • 6
  • 17
Jts
  • 3,447
  • 1
  • 11
  • 14
  • 1
    `std::forward(other._data)` won't compile, so you can't use that instead – Jonathan Wakely Feb 12 '16 at 22:14
  • @JonathanWakely `std::forward` is the same as `std::move` if `T` did not come from a forwarding reference – M.M Feb 12 '16 at 22:36
  • @M.M but redundant, just use std::move in that case. – Jonathan Wakely Feb 13 '16 at 12:41
  • 1
    @José, no, it doesn't compile. Your example doesn't instantiate the template, because it doesn't use that constructor. Try calling it and it will fail to compile. `std::forward` **must** be used with a template argument list to say what type to forward as. – Jonathan Wakely Feb 13 '16 at 12:52
1

std::forward should be used with a forwarding reference.
std::move should be used with an rvalue reference.

There is nothing particular about constructors. The rules apply the same to any function, member function or constructor.

The most important thing is to realize when you have a forwarding reference and when you have an rvalue reference. They look similar but are not.

A forwarding reference is always in the form:

T&& ref

for T some deduced type.

For instance, this is a forwarding reference:

template <class T>
auto foo(T&& ref) -> void;

All these are rvalue references:

auto foo(int&& ref) -> void; // int not deduced

template <class T>
auto foo(const T&& ref); // not in form `T&&` (note the const)

template <class T>
auto foo(std::vector<T>&& ref) -> void; // not in form `T&&`

template <class T>
struct X {
    auto foo(T&& ref) -> T; // T not deduced. (It was deduced at class level)
};

For more please check this excellent in-depth article by Scott Meyers with the note that when the article was written the term "universal reference" was used (actually introduced by Scott himself). Now it is agreed that "forwarding reference" better describes it's purpose and usage.


So your example should be:

ClassName(ClassName&& other)
   : _data(std::move(other._data))
{
}

as other is an rvalue reference because ClassName is not a deduced type.

bolov
  • 72,283
  • 15
  • 145
  • 224
  • The link to Scott's article is dead or simply incorrect. Please check that – Patryk Feb 13 '16 at 12:26
  • @Patryk ty, don't know what happened. I put the correct link now. – bolov Feb 13 '16 at 12:39
  • I personally think the universal reference having to have the exact T&& type parameter is too subtle and thus elusive. Good that I understand it now. – user557583 Feb 14 '16 at 08:24