So I was just writing a sample and contrived example of std::forward
for my understanding, but it didn't work the way I expected. In the program below
#include <string>
#include <iostream>
struct A
{
A(const std::string& m) : m_(m) {};
std::string m_;
};
template<typename T>
void funcB(T&& obj) // universal reference
{
std::string local = std::move(obj.m_); // using std::move on universal reference. Bad.
std::cout << "funcB : " << local << '\n';
}
template<typename T>
void funcC(T&& obj) // universal reference
{
std::string local = std::forward<std::string>(obj.m_); // using std::move on universal reference
std::cout << "funcC : " << local << '\n';
}
template<typename T>
void funcD(T&& obj) // universal reference
{
T local = std::forward<T>(obj); // using std::move on universal reference
std::cout << "funcD : " << local.m_ << '\n';
}
int main()
{
A obj("firstString");
//funcA(obj); // We get compiler error. Rvalue reference cannot be bound to Lvalue
funcB(obj);
std::cout << "Main : " << obj.m_ << '\n';
A obj2("secondString");
funcC(obj2);
std::cout << "Main : " << obj2.m_ << '\n';
A obj3("thirdString");
funcD(obj3);
std::cout << "Main : " << obj3.m_ << '\n';
}
In the output
funcB : firstString
Main :
funcC : secondString
Main :
funcD : thirdString
Main : thirdString
In funcC
, even though I have used universal references and here it is bound to Lvalue, the string is moved when I do std::forward<std::string>
. Hence the in the last line, after "Main:" there is no output. Can someone please how the string is moved even though obj is bound to Lvalue.
Just realized the answer to this, after rereading a book.
In funcC, std::forward<std::string>(obj.m_)
is equivalent to moving the string.
But in funcD, std::forward
is instantiated as std::forward<struct A&>(obj)
, which is copied.
In both cases you are constructing a string, std::string local
, with an rvalue reference as argument. As a result, local
is move-constructed from the original object referred-to by that reference.
This doesn't have much to do with std::forward
or with std::move
; furthermore, you are not initialising a reference (which is where "binding a reference to an lvalue" comes from in Meyers's text; the reference would be on the LHS) — you are simply constructing an object from another object.
However, it is true that without writing std::forward
or std::move
you would end up copying those function arguments instead, as the rvalue reference would be dropped on the initialisation side of local
's declaration.
So, in this case, std::forward
and std::move
have the same effect. However, they are not the same thing and should generally not be treated as interchangeable. Read the following for more information: