I would expect that the existence (or absence) of the line with the default copy-constructor in the following example should make some difference, but the behavior (on Windows) is the same in both cases:
template <class T>
struct Vec {
T x = T();
T y = T();
Vec() = default;
Vec(const Vec&) = default; // default copy ctor
template <class Other>
Vec(const Other &o) // templated copy ctor
: x(o.x), y(o.y)
{
cout << "templated copy ctor";
}
};
Vec<int> i;
Vec<double> d = i; // calls templated 'copy ctor'
Vec<double> dd = dd; // does not call templated copy ctor, whether 'default copy ctor' exists, or not !
It seems that the compiler always generates a default constructor (that is if I do not explicitly delete it, or make it protected), and therefore the templated copy ctor never matches.
If the compiler always generates a default constructor, why would I ever want to write this line?
Why not always omit it instead?
Vec(const Vec&) = default; // default copy ctor
What would be a wise coding standard ?
to always type the default copy ctor, or
to always omit typing the default copy ctor?
It seems that the compiler always generates a default constructor (that is if I do not explicitly delete it, or make it protected), and therefore the templated copy ctor never matches.
Yes: the copy constructor isn't template so is a best match for a Vec const &.
As Some programmer dude observed in a comment, the template one isn't (to be exact) a copy-constructor.
If the compiler always generates a default constructor, why would I ever want to write this line?
Not always.
The rules regarding deleted/defaulted constructors and deleted/defaulted assignment operators are complicated so, for example, the copy-constructor is implicitly deleted when there is a user declared move constructor or a user declared move assignment.
In that case, a = default can re-enable the default copy constructor.
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.
Ok, I am starting to get the jist of rvalue references (I think). I have this code snippet that I was writing:
#include <iostream>
using namespace std;
std::string get_string()
{
std::string str{"here is your string\n"};
return std::move(str); // <----- cast here?
}
int main ()
{
std::string my_string = std::move(get_string()); // <----- or cast here?
std::cout << my_string;
return 0;
}
So I have a simple example where I have a function that returns a copy of a string. I have read that its bad (and got the core-dumps to prove it!) to return any reference to a local temp variable so I have discounted trying that.
In the assignment in main() I don't want to copy-construct that string I want to move-construct/assign the string to avoid copying the string too much.
Q1: I return a "copy" of the temp var in get_string() - but I have cast the return value to rvalue-red. Is that pointless or is that doing anything useful?
Q2: Assuming Q1's answer is I don't need to do that. Then am I moving the fresh copy of the temp variable into my_string, or am I moving directly the temp variable str into my_string.
Q3: what is the minimum number of copies that you need in order to get a string return value stored into an "external" (in my case in main()) variable, and how do you do that (if I am not already achieving it)?
I return a "copy" of the temp var in get_string() - but I have cast the return value to rvalue-red. Is that pointless or is that doing anything useful?
You don't have to use std::move in that situation, as local variables returned by value are "implicitly moved" for you. There's a special rule in the Standard for this. In this case, your move is pessimizing as it can prevent RVO (clang warns on this).
Q2: Assuming Q1's answer is I don't need to do that. Then am I moving the fresh copy of the temp variable into my_string, or am I moving directly the temp variable str into my_string.
You don't need to std::move the result of calling get_string(). get_string() is a prvalue, which means that the move constructor of my_string will automatically be called (pre-C++17). In C++17 and above, mandatory copy elision will ensure that no moves/copies happen (with prvalues).
Q3: what is the minimum number of copies that you need in order to get a string return value stored into an "external" (in my case in main()) variable, and how do you do that (if I am not already achieving it)?
Depends on the Standard and on whether or not RVO takes place. If RVO takes place, you will have 0 copies and 0 moves. If you're targeting C++17 and initializing from a prvalue, you are guaranteed to have 0 copies and 0 moves. If neither take place, you'll probably have a single move - I don't see why any copy should occur here.
You do not need to use std::move on the return value which is a local variable. The compiler does that for you:
If expression is an lvalue expression that is the (possibly parenthesized) name of an automatic storage duration object declared in the body or as a parameter of the innermost enclosing function or lambda expression, then overload resolution to select the constructor to use for initialization of the returned value is performed twice: first as if expression were an rvalue expression (thus it may select the move constructor), and if no suitable conversion is available, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object's type (possibly cv-qualified), overload resolution is performed a second time, with expression considered as an lvalue (so it may select the copy constructor taking a reference to non-const).
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).
In the following example, why doesn't the move constructor get called in the construction of 'copy' inside fun, even though the 'src' argument of 'fun' is explicitly a rvalue reference and is only used in that construction?
struct Toy {
int data;
Toy(): data(0)
{
log("Constructed");
}
Toy(Toy const& src): data(src.data)
{
log("Copy-constructed");
}
Toy(Toy&& src): data(src.data)
{
log("Move-constructed");
}
};
Toy fun(Toy&& src)
{
Toy copy(src);
copy.data = 777;
return copy;
}
Toy toy(fun(Toy())); // LOG: Constructed Copy-constructed
While Bob && b is an rvalue reference, all named use of data after construction is using it as an lvalue.
So Bob&& b will only bind to rvalues, but when you use it it will not move.
The only ways to get an rvalue reference are:
A value without a name, such as a temporary return value or result of a cast.
Use of a local value variable in a simple return x; statement.
Explicitly casting to an rvalue, such as with std::move or std::forward.
This prevents data from being silently moved from on one line and then used on the next. It can help to think of rvalue as being 'I the programmer say this is not needed after this expression' at use, and 'only take things that are not needed afterwards' in function parameters. The temporary/return exceptions above are two spots the compiler can relatively safely guarantee this itself.
Finally, note that universal references (auto&& and T&&) look like rvalue references but sometimes are not.