Implicitly-declared Move-Operations do not fallback to Copy? - c++

Do I read N3291 "12.8.(11/15/28) Copying and moving class objects class.copy]" correct that the implicitly-declared move constructor
does an element-wise move of all non-static data-members (probably via respectively defined T(T&&)
and if any non-static data-member can not be moved, the implicit move-constructor will be marked as deleted and not tried to be copied as a "fallback"? (yes, move is defined for built-in types, but actually is a copy).
and likewise the move-assign, using the respective T operator=(T&&) of the elements.
Example:
struct CopyOnly {
CopyOnly();
CopyOnly(const CopyOnly&);
}; // declaring a copy means no implicit move.
struct Question {
std::vector<int> data_;
CopyOnly copyOnly_;
};
The class Question
will have implicitly-declared copy-constructor and assign
will have implicitly-declared move-constructor and move-assign, but they will be =deleted, because the non-static data-member data_ is only copyable, but not movable?
Update. A side-question: For Question q; will std::move(q) still work? Will the fallback to copy happen there? Or will the implicitly-declared move-ctor force the compiler to stop with an error? Here it does compile.
Update 2. What does the compiler generate for the non-movable data-members if I declare the move-ctor Question(Question&&) =default? Does it then fallback to copying those?

You read it incorrectly. This would break lots of C++03 classes in cases such as the following
Question getQuestion();
Question q(getQuestion()); // use of deleted move constructor!
Instead, the FDIS says that a move constructor will be declared iff {there is no user declared {copy constructor, {copy, move} assignment operator, destructor} and the implicitly declared move constructor would not be defined as deleted}.
Regarding Update 2. It has been brought to my attention that if you explicitly-default the move constructor, it will be defined as deleted by the condition
for the move constructor, a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.
In the following, the move constructor will be defined as deleted, because CopyOnly is not trivially copyable.
struct Question
{
std::vector<int> data_;
CopyOnly copyOnly_;
Question(Question&&) = default;
};

Related

can I use std::move with class that doesn't provide a move constructor?

I have a class like this:
class myClass
{
int x[1000];
public:
int &getx(int i)
{
return x[i];
}
}
Please note that I did not provide a move construct here.
if I use the following code:
myClass A;
auto B=std::move(a);
does A moves to B or since I did not provided a move constructor, A is copied to B?
Is there any default move constructor for an object? If yes, how it does work with pointers and dynamically allocated arrays?
Although you did not provide explicit move constructor, the compiler provided implicit move constructor to you.
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
X does not have a user-declared copy constructor, and
X does not have a user-declared copy assignment operator,
X does not have a user-declared move assignment operator,
X does not have a user-declared destructor, and
the move constructor would not be implicitly defined as deleted.
So the answer on your question is: no, A is not copied to B.
It is not clear what you ask in other questions. Please specify more details.
Is there any default move constructor for an object?
Yes in your case. For any type T, a move constructor is implicitly declared only if some conditions are met. More details can be seen at cpperference.com.
If yes, how it does work with pointers and dynamically allocated arrays?
The default implementation will make a shallow copies of pointers. As a consequence, more than one object will point to dynamically allocated arrays. That is going lead to problems. See The Rule of Three for more on the subject.
If you have pointers that point to dynamically allocated arrays, you need to:
Provide an explicitly defined copy constructor that does the right thing with the dynamically allocated arrays. The side effect of this will be that the default move constructor will be implicitly deleted. and/or
Provide an explicitly defined move constructor where you move the ownership of the dynamically allocated arrays appropriately.
Move constructor is generated for your class as you don't define any method which avoid its generation (as user defined destructor or copy constructor)
The auto generated move constructor would move each member. For int[1000] it is equivalent to copy.
If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:
There are no user-declared copy constructors.
There are no user-declared copy assignment operators.
There are no user-declared move assignment operators.
There are no user-declared destructors
then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature T::T(T&&).
Quote from cppreference
Basically, yes a default move constructor is created as long as you don't define a copy constructor, copy assignment overload, move assignment overload, or destructor. Otherwise you have to either define the behavior yourself or use:
class_name ( class_name && ) = default;
which will explicitly declare the default version of the move constructor.
Your question is similar to this one. Note that std::move simply is a cast. Your value a will be cast to an rvalue reference, but in your case, the original object will not be plundered or pilfered.
To use an API or an idiom correctly, you have to use it for the right thing. Moving makes most sense for e.g. class objects where the bulk of the object data is allocated on the heap. Such an object is easy to pilfer, and the 'move' can leave the pilfered object in a well-defined state. The canonical example could be e.g. some type of string class with string data on the heap.
In your case, what would you like to happen? Assume a is a local var in some function, i.e. assume a is on the stack. Assume b is a global or file scope or anonymous namespace variable, i.e. not on the stack. What would you like to happen when moving from a to b?
For a string, pilfering makes sense. For your case, pilfering is nonsensical.

Is an empty constructor with initializer list considered trivial?

Is the following constructor considered trivial?
struct A
{
A() : a(nullptr) {}
private:
int* a;
};
These examples makes me a little confused. With c++11 this should also be possible:
struct A
{
private:
int* a{nullptr};
};
and a should be properly initialized to nullptr. Here I did not define the constructor, but it should have the same form as the first implementation. Is either of these classes considered trivial?
The purpose of me asking is if I can expect move/copy constructors and assignment operators being automatically generated.
Is the following constructor considered trivial?
A() : a(nullptr) {}
No, because it is user-defined.
struct A
{
private:
int* a{nullptr};
};
No, because it has brace initializer for a non-static member.
According to the standard (emphasis mine):
12.1 Constructors....
A default constructor for a class X is a constructor of class X that can be called without an argument. If
there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted (8.40).....
A default constructor is trivial if it is not user-provided and if:....
— no non-static data member of its class has a brace-or-equal-initializer, and....
Otherwise, the default constructor is non-trivial.
The purpose of me asking is if I can expect move/copy constructors and assignment operators being automatically generated.
As #M.M and #NicolBolas commented, generation of these constructors and operators are not impacted by existence of the trivial constructor.
The rules are a bit complicated and not very consistent.
The copy constructor is generated only if there is no explicitly declared one. (And it is deleted if the move constructor or the move assignment operator is declared.)
Similarly, the copy assignment operator is generated only if there is no explicitly declared one. (And again, it is deleted if the move constructor or the move assignment operator is declared.)
The move constructor is generated only if there are no explicitly declared move constructor, move operator, copy constructor, copy assignment and destructor.
The same rule is for the move assignment operator.

Will move constructor and move assignment be generated if I default the copy constructor?

I am a little confused about how best to define a copyable but not moveable class. It seems to me that deleting the move constructor is a bad idea because then I couldn't construct from a temporary. On the other hand I don't want the compiler to implicitly generate a move constructor for me that may be wrong or may lead to unexpected behavior. My question is whether the compiler is allowed to implicitly generate a move constructor for the following class:
class C {
public:
int i_;
C(const C&) = default;
}
[class.copy]/9 after CWG 1402:
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
X does not have a user-declared copy constructor,
X does not have a user-declared copy assignment operator,
X does not have a user-declared move assignment operator, and
X does not have a user-declared destructor.
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
X does not have a user-declared copy constructor
[and ...]
Since the copy constructor is user-declared (even though it is defaulted, which means it is not user-provided), a move constructor will not be implicitly declared as defaulted.
You're right that if you were to explicitly delete the move constructor, you would not be able to construct from temporaries because the deleted move constructor would be chosen by overload resolution. So the approach you have used for a copyable but not moveable class is the right one.
Relative to your example the compiler will implicitly declare a move constructor because the first declaration of your copy constructor is the default definition. In this case the copy constructor is considered as implicitly defined. But if you would change your class definition the following way
class C {
public:
int i_;
C(const C&);
};
C::C( const C & ) = default;
then in this case the move constructor would not be generated by the compiler becasue the copy constructor is explicitly declared by the user.

C++11 move constructor

What would be the correct way to implement a move constructor considering the following class:
class C {
public:
C();
C(C&& c);
private:
std::string string;
}
Of course, the idea is to avoid copying string or deallocating it twice.
Lets assume the basic example is just for clarity and I do need a move constructor.
I tried:
C::C(C&& c) {
//move ctor
string = std::move(c.string);
}
And
C::C(C&& c) : string(std::move(c.string)) {
//move ctor
}
Both compile fine on gcc 4.8 and run fine. It seems option A is the correct behaviour, string gets copied instead of moved with option B.
Is this the correct implementation of a move constructor?
Since std::string itself has a move-ctor, the implicitly defined move-ctor for C will take care of the proper move operation. You may not define it yourself. However, if you have any other data member and specifically:
12.8 Copying and moving class objects
12 An implicitly-declared copy/move constructor is an inline public
member of its class. A defaulted copy- /move constructor for a class X
is defined as deleted (8.4.3) if X has:
— a variant member with a
non-trivial corresponding constructor and X is a union-like class,
— a
non-static data member of class type M (or array thereof) that cannot
be copied/moved because overload resolution (13.3), as applied to M’s
corresponding constructor, results in an ambiguity or a function that
is deleted or inaccessible from the defaulted constructor, or
— a
direct or virtual base class B that cannot be copied/moved because
overload resolution (13.3), as applied to B’s corresponding
constructor, results in an ambiguity or a function that is deleted or
inaccessible from the defaulted constructor, or
— for the move
constructor, a non-static data member or direct or virtual base class
with a type that does not have a move constructor and is not trivially
copyable.
13 A copy/move constructor for class X is trivial if it is
neither user-provided nor deleted and if
— class X has no virtual functions (10.3) and no virtual base classes (10.1), and
functions (10.3) and no virtual base classes (10.1), and
— the
constructor selected to copy/move each direct base class subobject is
trivial, and
— for each non-static data member of X that is of class
type (or array thereof), the constructor selected to copy/move that
member is trivial; otherwise the copy/move constructor is non-trivial.
you may want to implement your own move-ctor.
In case you need the move-ctor, prefer the initializer list syntax. Always! Otherwise, you may end up with a default construction per object not mentioned in the initializer list (which is what you're forced for member objects with non-default ctors only).
Both your variants will move the string. The second variant should be preferred because it will not default construct an empty string just to move assign it afterwards.
Check your testcase and then your compiler's bugzilla list. You need to trace calls to both string::operator=(string&&) (1st case) and string::string(string&&) (2nd case) if you want to ensure for both cases that they move.
Both Constructors should work. So both are correct move constructors. The second one might be more efficient, since the first one default constructs string only to assign to it, while the second will simply move construct it and should therefore be more efficient. If the second one is less efficient I would suspect a compiler bug (remember that C++11 support is still not complete for current compilers) or a flawed test methology (how exactly do you test copy vs move and are you sure the move constructor not the assignment op is called in both cases?).
Of course whenever possibly you could simply let the compiler generate your constructor via C(C&&) = default;.
There is no need to implement a move constructor here since you don't have to manually manage memory. Move constructors are only useful when you manually use dynamic arrays in your class.
You can still explicitly have the compiler create the default move constructor even though it should have already been done even if you don't request it:
C(C&& c) = default;

C++: Is default copy constructor affected by presence of other constructors and destructor?

As we know, if any constructor is declared (copy constructor included), default constructor (the one that takes no arguments) is not implicitly created. Does the same happen with a default copy constructor (the one that performs shallow copy of an object)? Also, does the presence of destructor affect this anyhow?
12.8 #4 Copying class objects
If the class definition does not
explicitly declare a copy constructor,
one is declared implicitly
And the destructor plays no part
The answers here are correct but not complete. They are correct for C++98 and C++03. In C++11 you will not get a copy constructor if you have declared a move constructor or move assignment operator. Furthermore if you have declared a copy assignment operator or a destructor, the implicit generation of the copy constructor is deprecated. 12.8 [class.copy]:
If the class definition does not
explicitly declare a copy constructor,
there is no user-declared move
constructor, and there is no
user-declared move assignment
operator, a copy constructor is
implicitly declared as defaulted
(8.4.2). Such an implicit declaration
is deprecated if the class has a
user-declared copy assignment operator
or a user-declared destructor.
No. You'll get a default copy constructor unless you supply your own copy constructor, and the presence or absence of a destructor makes no difference.
No. And note that
MyClass
{
template <typename T> MyClass(const T&);
};
does not provide a copy constructor, and a default one is generated.
The default copy constructor is always created, unless you define your own one. The constructor with no arguments isn't defined with any other constructor present to avoid calling it and therefore skipping the real constructor(s)'s code.