Is an empty constructor with initializer list considered trivial? - c++

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.

Related

What's with the copy-constructor if the class contains a user-declared destructor?

The Standard in section 12.8/7 says:
If the class definition does not explicitly declare a copy
constructor, one is declared implicitly. If the class definition
declares a move constructor or move assignment operator, the
implicitly declared copy constructor is defined as deleted; otherwise,
it is defined as defaulted (8.4). The latter case is deprecated if the
class has a user-declared copy assignment operator or a user-declared
destructor. Thus, for the class definition
struct X {
X(const X&, int);
};
a copy constructor is implicitly-declared. If the user-declared
constructor is later defined as
X::X(const X& x, int i =0) { /∗ ... ∗/ }
I can't get the point of that The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor. In the example the Standard neither provides a user-declared copy assignment operator nor a destructor. What will happen if we declare a destructor or a copy assignment operator? I've tried to do that as follows:
struct A
{
~A(){ };
};
A::A(const A&){ }; //error
int main(){ }
DEMO
but in the example we still have the implicitly-declared copy constructor. What does that rule actual mean?
I thought that if we write the following:
struct A
{
A(){ };
A(const A&&){ };
~A(){ };
};
A a;
A t = a; //error: call to implicitly-deleted copy constructor of 'A'
int main()
{
}
DEMO
the copy-constructor won't explicitly deleted. But thats's not the case.
This deprecation basically incorporates the Rule of Three (Five). If a user-declared copy assignment operator or destructor is provided, the fact that the copy constructor is defined as defaulted (and not as deleted) is deprecated. That should prevent you from depending upon such an implicitly declared copy constructor in future.
In the example the Standard provides neither copy assignment nor
destructor are user-decalred.
The example has nothing to do with the deprecation.
I've tryied to do that as follows: […] but in the example we still have the impliclty-declared copy
constructor.
You cannot define an implicitly declared copy constructor - because it's already defined, by = default (no pun intended). You would have to declare it yourself first.
I thought that If we wirte the following: […] the copy-constructor won't explicitly deleted. But it's not true.
You quoted the rule that explicitly specifies that the copy constructor will be implicitly defined as deleted if a move constructor is declared:
If the class definition declares a move constructor or move
assignment operator, the implicitly declared copy constructor is
defined as deleted;
Clearly,
A(const A&&){ }
Is a move constructor according to [class.copy]/3. If you removed this move constructor then your example compiles, although it uses said deprecated feature.
Deprecated normally means that something will work, but that it is frowned upon and may not work in the future. I think the standard is saying that if you create a user-declared copy assignment operator or a user-declared destructor it will still create a default copy constructor (if you haven't) - but it may not in the future. So they want you to create your own copy constructor now, if you have one of the other two, and in the future they may force you to.

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.

destructor and unique_ptr

I have the following code
class A {
public:
A(){}
~A(){}
private:
std::vector<std::unique_ptr<double> > x;
};
A f() {
A a;
return a;
}
int main() {
A a=f();
return 0;
}
It does not compile (gcc 4.7), unless I comment out the destructor. Actually, I don't really need this destructor in my code, I just wanted to use it for debug purpose.
However, I don't understand what is happening and I therefore fear I have done something wrong. What is happening here ?
That is because the presence of an explicitly defined destructor prevents the implicit generation of a move constructor for A.
Per Paragraph 12.8/9 of the C++11 Standard:
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,
— X does not have a user-declared destructor, and
— the move constructor would not be implicitly defined as deleted.
Now without a move constructor, to return the value from f() the compiler will try to invoke the implicitly generated copy constructor (which is still being generated for backwards compatibility). However, std::unique_ptr is non-copyable. Hence, the error.
Explicitly defining a move constructor (or declaring one as defaulted, as suggested by juanchopanza in the comments) will fix the problem.

Are move constructors/assignment operators generated for derived classes

If class X is derived from class Y and class Y has any of:
A user-declared copy constructor,
A user-declared copy assignment operator,
A user-declared destructor
A user-declared move constructor,
A user-declared move assignment operator,
Will a move constructor and move assignment operator be implicitly defaulted for class X providing it declares none of the above?
e.g.
struct Y
{
virtual ~Y() {}
// .... stuff
};
struct X : public Y
{
// ... stuff but no destructor,
// no copy/move assignment operator
// no copy/move constructor
// will X have a default move constructor / assignment operator?
};
I am currently using gcc, but I am mainly interested in what the correct behaviour should be (as opposed to whether or not a particular compiler is standards compliant)..
As far as the derived class is concerned, the fact that an attribute or a base class has user-defined or implicit special functions does not matter. All that matter is their presence.
Compliant C++11 compilers should automatically provide the move constructors and assignment operators for the struct and class, if possible (which is clearly defined in the Standard), even though only classes with dynamically allocated buffers will really benefit from it (moving an int is just copying it).
Therefore, if your class embeds a std::string or a std::unique_ptr (for example), then its move constructor will call the embedded string or unique_ptr move constructor and it will be efficient... for free.
Simply changing the compilation mode should thus slightly improve performance.
Yes, they are, in §12.8.9:
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,
X does not have a user-declared destructor, and
the move constructor would not be implicitly defined as deleted.
and §12.8.10
The implicitly-declared move constructor for class X will have the form
X::X(X&&)
Similarly, for move assignment operators in 12.8.20:
If the definition of a class X does not explicitly declare a move assignment operator, 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 move constructor,
X does not have a user-declared copy assignment operator,
X does not have a user-declared destructor, and
the move assignment operator would not be implicitly defined as deleted.
Base classes don't come into it directly.

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

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;
};