I am currently reading about perfect forwarding and it states that the purpose of perfect forwarding is to pass lvalues as lvalues and rvalues as rvalues. So I am using the following example to understand perfect forwarding . The foo object here has has a copy and move cnstr and it also has a copy and move assignment operator.
void mfoo(foo arg)
{
.......
}
template<typename T>
void relay(T&& arg)
{
mfoo(std::forward<T>(arg));
}
//Start here
foo f;
relay(std::move(f));
Now I am testing this code in VS2012 and the move constructor is called only once . I was expecting it to be called twice. I would appreciate it if someone could explain this to me.
I expected the first move constructor to be called at relay and the next move constructor to be called at mfoo
//Start here
foo f; // default-initialize `f`, call the default constructor
relay(std::move(f));
std::move(f) does the same as static_cast<foo&&>(f), so it actually doesn't "do" anything; it merely produces an rvalue expression ("f is treated as an rvalue").
relay( std::move(f) ) instantiates void relay<foo>(foo&& arg) and calls this function. The parameter is a reference and reference-compatible with the argument, hence no constructor is called.
mfoo(std::forward<T>(arg))
std::forward<T>(arg) yields arg as an lvalue if T is an lvalue reference, or as an rvalue if T is not an lvalue reference. I.e., for T == foo&&, it does the same as std::move(arg). No constructor is called.
mfoo(..) finally calls mfoo, which takes its argument by value. This causes a copy or move, depending on the type and the value category of the argument expression.
As our argument expression here is an rvalue, the move constructor will be called (if existent and accessible).
Related
Here is the code:
class SomeType {
public:
SomeType() {}
~SomeType() {}
std::string xxx;
}
bool funtion_ab() {
SomeType(); // This is a right val;
// The right val destructs here when I test the code. I want to make sure that it would always destructs here.
int a = 0, b = 10;
....// other code
return true;
}
Please tell me if you know the truth. Thank you!
What you have is called a temporary object. From §6.7.7,
Temporary objects are created
when a prvalue is converted to an xvalue
or, more specifically,
[Note 3: Temporary objects are materialized:
...
when a prvalue that has type other than cv void appears as a discarded-value expression ([expr.context]).
— end note]
and, on the lifetime, the same section has this to say
Temporary objects are destroyed as the last step in evaluating the full-expression ([intro.execution]) that (lexically) contains the point where they were created.
You can read more about the expression semantics, but in your case "full-expression" is fairly unambiguous.
SomeType();
The "full-expression" containing your constructor call is... the constructor call itself. So the destructor will be called immediately after evaluating the constructor. There are some exceptions to this rule (such as if the temporary object is thrown as an exception or is bound as a reference), but none of those apply here.
As noted in the comments, compilers are free to inline your constructor and destructor calls and then are free to notice that they do nothing and omit them entirely. Optimizers can do fun stuff with your code, provided it doesn't change the semantics. But a strict reading of the standard states that the destructor is called exactly where you suggested.
I've come across the following code:
class X {
std::vector<int> m_v;
public:
X(std::vector<int>&& v) : m_v(std::move(v)) {
};
Parametet v is already an rvalue-reference - so why do we need to apply std::move to it?
In short: every named object is Lvalue, and even if v is reference to Rvalue you need to use move to force move ctor to be called.
From reference - value categories
Even if the variable's type is rvalue reference, the expression
consisting of its name is an lvalue expression;
now your data member m_v is vector which contains copy and move constructor.
Next sentences describe which one is called, reference:
If both copy and move constructors are provided and no other
constructors are viable, overload resolution selects the move
constructor if the argument is an rvalue of the same type (an xvalue
such as the result of std::move or a prvalue such as a nameless
temporary (until C++17)), and selects the copy constructor if the
argument is an lvalue (named object or a function/operator returning
lvalue reference).
So when you write:
m_v(std::move(v)) // move ctor is called because you are passing Xvalue
but in this:
m_v(v) // copy ctor is called because v as named object is passed
I can't understand the function move in c++11.
From here, I got things below:
Although note that -in the standard library- moving implies that the
moved-from object is left in a valid but unspecified state. Which
means that, after such an operation, the value of the moved-from
object should only be destroyed or assigned a new value; accessing it
otherwise yields an unspecified value.
In my opinion, after move(), the moved-from object has been "clear". However, I've done a test below:
std::string str = "abcd";
std::move(str);
std::cout<<str;
I got abcd on my screen.
So has the str been destroyed? If so, I could get abcd because I'm just lucky? Or I misunderstood the function move?
Besides, when I read C++ Primer, I got such a code:
class Base{/* ... */};
class D: public Base{
public:
D(D&& d): Base(std::move(d)){/* use d to initialize the members of D */}
};
I'm confused now. If the function move will clear the object, the parameter d will be clear, how could we "use d to initialize the members of D"?
std::move doesn't actually do anything. It's roughly analogous to a cast expression, in that the return value is the original object, but treated differently.
More precisely, std::move returns the object in a form which is amenable to its resources being 'stolen' for some other purpose. The original object remains valid, more or less (you're only supposed to do certain special things to it, though that's primarily a matter of convention and not necessarily applicable to non-standard-library objects), but the stolen-away resources no longer belong to it, and generally won't be referenced by it any more.
But! std::move doesn't, itself, do the stealing. It just sets things up for stealing to be allowed. Since you're not doing anything with the result, let alone something which could take advantage of the opportunity, nothing gets stolen.
std::move doesn’t move anything. std::move is merely a function template that perform casts. std::move unconditionally casts its argument to an rvalue,
std::move(str);
With this expression you are just doing type cast from lvalue to rvalue.
small modification in program to understand better.
std::string str = "abcd";
std::string str1 = std::move(str);
std::cout<<str<<std::endl;
std::cout<<str1<<std::endl;
str lvalue typecast to rvalue by std::move, std::string = std::move(str); =>this expression call the string move constructor where actual stealing of resources take placed. str resources(abcd) are steeled and printed empty string.
Here is sample implementation of move function. Please note that it is not complete implementation of standard library.
template<typename T> // C++14; still in
decltype(auto) move(T&& param) // namespace std
{
using ReturnType = remove_reference_t<T>&&;
return static_cast<ReturnType>(param);
}
Applying std::move to an object tells the compiler that the object is eligible to be moved from. It cast to the rvalue.
class Base{/* ... */};
class D: public Base{
public:
D(D&& d): Base(std::move(d)){/* use d to initialize the members of D */}
};
Base(std::move(d)) it will do up-casting only move the base class part only.
Here one more interesting thing to learn for you. If you do not invoke base class destructor with std::move like D(D&& d): Base(d) then d will be considered as lvalue and copy constructor of Base class involved instead of move constructor. Refer for more detail Move constructor on derived object
It is my understanding that move semantics can use move-constructors to elide what would otherwise be a copy. For example, a function returning a (perhaps) large data structure can now return by value, and the move constructor will be used to avoid a copy.
My question is this: is the compiler required to not copy when this is possible? It doesn't seem to be the case. In that case, wouldn't the following code have "implementation-defined" semantics?
static const int INVALID_HANDLE = 0xFFFFFFFF;
class HandleHolder {
int m_handle;
public:
explicit HandleHolder(int handle) : m_handle(handle) {}
HandleHolder(HandleHolder& hh) {
m_handle = hh.m_handle;
}
HandleHolder(HandleHolder&& hh) : m_handle(INVALID_HANDLE) {
swap(m_handle, hh.m_handle);
}
~HandleHolder() noexcept {
if (m_handle != INVALID_HANDLE) {
destroy_the_handle_object(m_handle);
}
}
};
Say then we make a function:
HandleHolder make_hh(int handle) { return HandleHolder(handle); }
Which constructor is called? I would expect the move constructor, but am I guaranteed the move constructor?
I'm aware this is a silly example and that -- for example -- the copy constructor of this object should be deleted because there is no way to use it safely otherwise, but the semantics are simple enough that I wouldn't think something like this would be implementation-defined.
Yes, of course. There's nothing implementation-defined about it.
If there is a move constructor and it can be used, and it is a choice between a move constructor and a copy constructor, the move constructor will be invoked. That is a guarantee.
[C++11: 13.3.3.2/3]: [..] Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if:
[..]
S1 and S2 are reference bindings (8.5.3) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.
[..]
I think your confusion stems from misuse of the term "elide". The compiler may elide copies/moves and replace them with nothingness — with in-place construction that bypasses the invocation of a constructor altogether. Copy elision never results in a move, and move elision never results in a copy. Either the object "transferral" happens or it does not.
You could sort of argue that your program has "implementation-defined" semantics in the sense that you don't know whether copies/moves will be elided until the program has been compiled, and because such elision is allowed to modify side-effects (such as console output). But we don't tend to think of it that way.
Regardless, this does not affect which of the copy and move constructors will be invoked if either are to be.
Your example is further flawed because only your move constructor can be invoked: your copy constructor takes a ref-to-non-const which can't be bound through an rvalue initialiser.
I've written a class similar to the following:
class ScriptThread {
public:
ScriptThread(): mParent() {}
private:
ScriptThread(ScriptThread *parent): mParent(parent) {}
public:
ScriptThread(ScriptThread &&rhs);
ScriptThread &operator = (ScriptThread &&rhs);
// copy constructor/assignment deleted implicitly
ScriptThread &execute(const Script &script);
ScriptThread spawn();
ScriptThread spawn(const Script &script);
private:
ScriptThread *mParent;
};
ScriptThread &ScriptThread::execute(const Script &script) {
// start executing the given script
return *this;
}
ScriptThread ScriptThread::spawn() {
// create a ScriptThread with "this" as its parent
return ScriptThread(this);
}
ScriptThread ScriptThread::spawn(const Script &script) {
// convenience method to spawn and execute at the same time
return spawn().execute(script); // ERROR: "use of deleted function"
}
As written, g++ fails to compile it at the line marked "ERROR", claiming that it's trying to use the (deleted) copy constructor. However, if I replace the last function with this:
ScriptThread ScriptThread::spawn(const Script &script) {
ScriptThread thread = spawn();
thread.execute(script);
return thread;
}
It compiles without an error. Even after referring to a number of articles, references, and other SO questions, I don't understand: why does the first invoke the copy constructor at all? Isn't the move constructor enough?
execute(script) returns an lvalue. You can't implicitly move from an lvalue, so to use the move constructor for the returned object you would need to say
return std::move(spawn().execute(script));
You didn't do this so it tries to use the copy constructor, because that's how you make new objects from lvalues.
In your replacement case you have:
return thread;
Here thread is also an lvalue, but it's about to go out of scope as soon as the function ends, so conceptually it can be considered to be like a temporary or other variable that is going to disappear at the end of the expression. Because of this there is a special rule in the C++ standard that says the compiler treats such local variables as rvalues, allowing the move constructor to be used even though thread is really an lvalue.
See Barry's more complete answer for the references to the standard where the special rule is defined, and the full details of the rule.
ScriptThread is noncopyable (the implicit copy constructor/assignment operators are defined as deleted because you declared move constructor/assignment). In spawn(), your original implementation:
ScriptThread ScriptThread::spawn(const Script &script) {
return spawn().execute(script);
}
is attempting to construct a ScriptThread from an lvalue reference (execute returns a ScriptThread&). That will call the copy constructor, which is deleted, hence the error.
However, in your second attempt:
ScriptThread ScriptThread::spawn(const Script &script) {
ScriptThread thread = spawn();
thread.execute(script);
return thread;
}
we run into the rule, from [class.copy]:
When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the
object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly
parenthesized) id-expression that names an object with automatic storage duration declared in the body or
parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution
to select the constructor for the copy is first performed as if the object were designated by an rvalue.
Even though thread is an lvalue, we perform overload resolution on the constructor of ScriptThread as if it were an rvalue. And we do have a valid constructor for this case: your move constructor/assignment.
That's why the replacement is valid (and uses move construction), but the original failed to compile (because it required copy construction).