Why can't a destructor be marked constexpr? - c++

In C++, you can declare many things as constexpr: variables, functions (including member functions and operators), constructors, and since C++1z, also if statements and lambda expressions. However, declaring a destructor constexpr results in an error:
struct X {
constexpr ~X() = default; // error: a destructor cannot be 'constexpr'
};
My questions:
Why can't a destructor be marked constexpr?
If I do not provide a destructor, is the implicitly generated destructor constexpr?
If I declare a defaulted destructor (~X() = default;), is it automatically constexpr?

As per the draft basic.types#10 possibly cv-qualified class type that has all of the following properties:
A possibly cv-qualified class type that has all of the following properties:
(10.5.1) - it has a trivial destructor,
(10.5.2) - it is either a closure type, an aggregate type, or has at
least one constexpr constructor or constructor template (possibly
inherited from a base class) that is not a copy or move constructor,
(10.5.3) - if it is a union, at least one of its non-static data
members is of non-volatile literal type
(10.5.4) - if it is not
a union, all of its non-static data members and base classes are of
non-volatile literal types.
Ques 1: Why a destructor cannot be marked as constexpr?
Because only trivial destructors are qualified for constexpr
Following is the relevant section of the draft
A destructor is trivial if it is not user-provided and if:
(5.4) — the destructor is not virtual,
(5.5) — all of the direct base classes of its class have trivial
destructors, and
(5.6) — for all of the non-static data members of its class that are
of class type (or array thereof), each such class has a trivial
destructor.
Otherwise, the destructor is non-trivial.
Ques 2: If I do not provide a destructor, is the implicitly generated destructor constexpr?
Yes, because implicitly generated destructor is trivial type, so it is qualified for constexpr
Ques 3: If I declare a defaulted destructor (~X() = default;), is it automatically constexpr?
Indeed, this destructor is user-declared and implicitly-generated and thus it is qualified for constexpr.
I'm not able to find any direct reference that only trivial destructors are qualified for constexpr but if the destructor is not trivial then it is for sure that class type is not cv-qualified. So it kind of implicit as you can't define a destructor for cv-qualified class.
C++20 Update
Since C++20, user defined destructors can also be constexpr under certain conditions.
dcl.constexpr/3:
The definition of a constexpr function shall satisfy the following
requirements:
its return type (if any) shall be a literal type;
each of its parameter types shall be a literal type;
it shall not be a coroutine ([dcl.fct.def.coroutine]);
if the function is a constructor or destructor, its class shall not have any
virtual base classes;
its function-body shall not enclose ([stmt.pre])
a goto statement,
an identifier label ([stmt.label]),
a definition of a variable of non-literal type or of static or thread
storage duration.

If what you're looking for is reasoning behind the restriction, have a look at this paper which clearly states that the restriction is artificial - there is no intrinsic property of destructors that prevent them from working in constexpr contexts, and indeed compiler implementors agree that supporting them in constexpr contexts will be trivial to implement.
I guess the C++ standards committee originally placed the restriction in C++11 because they didn't want to deal with destructors at that time and it was easier to just rule them out entirely.

Since C++20, a constructor may be marked constexpr; I don’t know if it says anywhere specifically “a destructor may be constexpr”, but the draft standard includes the following text in section 9.2.5 paragraph 5:
The definition of a constexpr destructor whose function-body is not = delete shall additionally satisfy the
following requirement:
for every subobject of class type or (possibly multi-dimensional) array thereof, that class type shall
have a constexpr destructor.
This also now has a useful function because C++20 also allows new and delete in constexpr contexts, allowing things like vector and string to work at compile time without hacks (although I believe C++20 does not actually include changes to the standard library to allow for this, it is possible to implement something with the same API and behaviour as vector that works completely at compile time).

Why a destructor cannot be marked as constexpr?
The C++11 standard is specific about use of constexpr for consructors and non-static member function. It does not say anything specific about destructor. One may assume that destructors are to be treated as non-static member functions.
constexpr can be used only for const member functions. Since a destructor cannot be const member function, it cannot be qualified as a constexpr member function.
If I do not provide a destructor, is the implicitly generated destructor constexpr.
Since use of
constexpr ~X() = default;
is an error, it makes sense to me that the compiler generated destructor is not a constexpr function. I can't find anything in the standard to justify my statement. I am guessing.
If I declare a defaulted destructor (~X() = default;), is it automatically constexpr
I think not. Once again, I can't find anything in the standard to justify my statement. I am guessing.
FWIW, g++ compiles and builds the following program just fine.
struct X {
constexpr X(int i) : i_(i) {}
~X() = default;
int i_;
};
int main()
{
const X x(10);
}

Reference say's:
constexpr destructors
In most cases, in order to create an object of a type T in a constant
expression, the destruction of T must be trivial. However, non-trivial
destructors are an important component of modern C++, partly due to
widespread usage of the RAII idiom, which is also applicable in
constexpr evaluations. Non-trivial destructors could be supported in
constant expressions, as follows:
Allow destructors to be marked as constexpr
Make defaulted destructors constexpr if they only invoke constexpr destructors
For constexpr variables, require that evaluating the destructor is a constant expression (except that the object being destroyed may be
modified in its own destructor
However, no compelling use cases are known for such a feature, and
there would be a non-trivial implementation cost ensuring that
destructors are run at the right times.

A destructor can't be constexpr because constexpr functions can't have side effects and destructors by definition are only useful through side effects. In short, it would be useless to have a destructor that is constexpr.
A object cannot be constexpr if its destructor is non-trivial. A defaulted one, if trivial, will be considered constexpr
Live
From [class.dtor]
Each decl-specifier of the decl-specifier-seq of a destructor declaration (if any) shall be friend, inline, or virtual.
Missing from it, constexpr. So you could just take it as: because the standard says soTM

Related

Does declaring a constexpr object marks the constructor as constexpr

I just a have a problem in understanding when the compiler marks the constructor as constexpr.
If I write the following program:
struct S{ S() {}; }
constexpr S s{ };
Does this mean that the default constructor is marked as constexpr?
An implicitly-defined constructor is a constructor defined by the compiler implicitly when some contexts are encountered (see below). But, an explicitly-defined constructor is a constructor defined by the user, not by the compiler.
Now per [class.default.ctor]/4:
A default constructor that is defaulted and not defined as deleted is
implicitly defined when it is odr-used ([basic.def.odr]) to create an
object of its class type ([intro.object]), when it is needed for
constant evaluation ([expr.const]), or when it is explicitly defaulted
after its first declaration. The implicitly-defined default
constructor performs the set of initializations of the class that
would be performed by a user-written default constructor for that
class with no ctor-initializer and an empty compound-statement. If
that user-written default constructor would be ill-formed, the program
is ill-formed. If that user-written default constructor would satisfy
the requirements of a constexpr constructor ([dcl.constexpr]), the
implicitly-defined default constructor is constexpr [..]
This paragraph just tells you that the non-deleted defaulted default constructor is implicitly-defined when it's odr-used or needed for constant evaluation or explicitly-defaulted after its first declaration.
Also, it tells you that that implicitly-defined constructor is the same as the user-written default constructor with an empty body and no member-initializer-list.
Then, it tells you that it's defined as constexpr if its corresponding user-written default constructor satisfies all of the [dcl.constexpr]/3 conditions.
That's, an implicitly or explicitly defaulted constructor will be implicitly-defined as constexpr if all requirements of [dcl.constexpr]/3 are met. On other hand, neither explicitly-defined nor explicitly-declared constructor is implicitly-defined as constexpr even if it satisfies all of [dcl.constexpr]/3 that's because you explicitly defined it. But if you explicitly mark it as constexpr, it will be a constexpr constructor, meanwhile, it shall satisfy all of [dcl.constexpr]/3 conditions.
So in your example:
struct S{ S() {}; }
constexpr S s{ };
That's ill-formed just because S is not a literal type and you're trying to call a non-constexpr constructor in a constant expression context which is not allowed per [const.expr]/(5.2)
A constructor is only (potentially) implicitly constexpr if either the whole constructor itself is implicitly declared or if it is defaulted with = default on its first declaration.
You are manually declaring the constructor and you are not defaulting it, so it will only be constexpr if you add the constexpr specifier to the declaration.
The shown constructor is therefore not constexpr and as a consequence constexpr S s{ }; will fail to compile because the initialization would call a non-constexpr constructor which isn't allowed in a constant expression. constexpr on a variable declaration does however require the initialization of the variable (including the evaluation of the initializer(s)) to be a constant expression.
The other answers give details about why the constructor in your snippet is not constexpr, and that's good to know.
However, I think, given how the question is phrased, that another answer is required.
Does declaring a constexpr object marks the constructor as constexpr
This means that you are thinking of the idea that the way you declare an object can affect the definition of its class.
That is not the case: once you write down the definition of a class, that's the definition, at it won't be altered by whatever objects of that class you declare/define.
In more complex scenario, when templates are involved, the code generated for a template class can depend on the objects you create; but the point is that you'd be talking of a templates class, whereas your question is about a class.

Why does aggregate initialization not work anymore since C++20 if a constructor is explicitly defaulted or deleted?

I'm migrating a C++ Visual Studio Project from VS2017 to VS2019.
I'm getting an error now, that didn't occur before, that can be reproduced with these few lines of code:
struct Foo
{
Foo() = default;
int bar;
};
auto test = Foo { 0 };
The error is
(6): error C2440: 'initializing': cannot convert from
'initializer list' to 'Foo'
(6): note: No constructor could take the source type, or
constructor overload resolution was ambiguous
The project is compiled with /std:c++latest flag. I reproduced it on godbolt. If I switch it to /std:c++17, it compiles fine as before.
I tried to compile the same code with clang with -std=c++2a and got a similar error. Also, defaulting or deleting other constructors generates this error.
Apparently, some new C++20 features were added in VS2019 and I'm assuming the origin of this issue is described in https://en.cppreference.com/w/cpp/language/aggregate_initialization.
There it says that an aggregate can be a struct that (among other criteria) has
no user-provided, inherited, or explicit constructors (explicitly defaulted or deleted constructors are allowed) (since C++17) (until C++20)
no user-declared or inherited constructors (since C++20)
Note that the part in parentheses "explicitly defaulted or deleted constructors are allowed" was dropped and that "user-provided" changed to "user-declared".
So my first question is, am I right assuming that this change in the standard is the reason why my code compiled before but does not anymore?
Of course, it's easy to fix this: Just remove the explicitly defaulted constructors.
However, I have explicitly defaulted and deleted very many constructors in all of my projects because I found it was a good habit to make code much more expressive this way because it simply results in fewer surprises than with implicitly defaulted or deleted constructors. With this change however, this doesn't seem like such a good habit anymore...
So my actual question is:
What is the reasoning behind this change from C++17 to C++20? Was this break of backwards compatibility made on purpose? Was there some trade off like "Ok, we're breaking backwards compatibility here, but it's for the greater good."? What is this greater good?
The abstract from P1008, the proposal that led to the change:
C++ currently allows some types with user-declared constructors to be initialized via aggregate initialization, bypassing those constructors. The result is code that is surprising, confusing, and buggy. This paper proposes a fix that makes initialization semantics in C++ safer, more uniform,and easier to teach. We also discuss the breaking changes that this fix introduces.
One of the examples they give is the following.
struct X {
int i{4};
X() = default;
};
int main() {
X x1(3); // ill-formed - no matching c’tor
X x2{3}; // compiles!
}
To me, it's quite clear that the proposed changes are worth the backwards-incompatibility they bear. And indeed, it doesn't seem to be good practice anymore to = default aggregate default constructors.
The reasoning from P1008 (PDF) can be best understood from two directions:
If you sat a relatively new C++ programmer down in front of a class definition and ask "is this an aggregate", would they be correct?
The common conception of an aggregate is "a class with no constructors". If Typename() = default; is in a class definition, most people will see that as having a constructor. It will behave like the standard default constructor, but the type still has one. That is the broad conception of the idea from many users.
An aggregate is supposed to be a class of pure data, able to have any member assume any value it is given. From that perspective, you have no business giving it constructors of any kind, even if you defaulted them. Which brings us to the next reasoning:
If my class fulfills the requirements of an aggregate, but I don't want it to be an aggregate, how do I do that?
The most obvious answer would be to = default the default constructor, because I'm probably someone from group #1. Obviously, that doesn't work.
Pre-C++20, your options are to give the class some other constructor or to implement one of the special member functions. Neither of these options are palatable, because (by definition) it's not something you actually need to implement; you're just doing it to make some side effect happen.
Post-C++20, the obvious answer works.
By changing the rules in such a way, it makes the difference between an aggregate and non-aggregate visible. Aggregates have no constructors; so if you want a type to be an aggregate, you don't give it constructors.
Oh, and here's a fun fact: pre-C++20, this is an aggregate:
class Agg
{
Agg() = default;
};
Note that the defaulted constructor is private, so only people with private access to Agg can call it... unless they use Agg{}, bypasses the constructor and is perfectly legal.
The clear intent of this class is to create a class which can be copied around, but can only get its initial construction from those with private access. This allows forwarding of access controls, as only code which was given an Agg can call functions that take Agg as a parameter. And only code with access to Agg can create one.
Or at least, that's how it is supposed to be.
Now you could fix this more targetedly by saying that it's an aggregate if the defaulted/deleted constructors are not publicly declared. But that feels even more in-congruent; sometimes, a class with a visibly declared constructor is an aggregate and sometimes it isn't, depending on where that visibly declared constructor is.
Towards a less surprising aggregate in C++20
To be on the same page with all readers, lets start by mentioning that aggregate class types make up a special family of class types that can be, particularly, initialized by means of aggregate initialization, using direct-list-init or copy-list-init, T aggr_obj{arg1, arg2, ...} and T aggr_obj = {arg1, arg2, ...}, respectively.
The rules governing whether a class is an aggregate or not are not entirely straight-forward, particularly as the rules have been changing between different releases of the C++ standard. In this post we’ll go over these rules and how they have changed over the standard release from C++11 through C++20.
Before we visit the relevant standard passages, consider the implementation of the following contrived class type:
namespace detail {
template <int N>
struct NumberImpl final {
const int value{N};
// Factory method for NumberImpl<N> wrapping non-type
// template parameter 'N' as data member 'value'.
static const NumberImpl& get() {
static constexpr NumberImpl number{};
return number;
}
private:
NumberImpl() = default;
NumberImpl(int) = delete;
NumberImpl(const NumberImpl&) = delete;
NumberImpl(NumberImpl&&) = delete;
NumberImpl& operator=(const NumberImpl&) = delete;
NumberImpl& operator=(NumberImpl&&) = delete;
};
} // namespace detail
// Intended public API.
template <int N>
using Number = detail::NumberImpl<N>;
where the design intent has been to create a non-copyable, non-movable singleton class template which wraps its single non-type template parameter into a public constant data member, and where the singleton object for each instantiation is the only that can ever be created for this particular class specialization. The author has defined an alias template Number solely to prohibit users of the API to explicitly specialize the underlying detail::NumberImpl class template.
Ignoring the actual usefulness (or, rather, uselessness) of this class template, have the author correctly implemented its design intent? Or, in other words, given the function wrappedValueIsN below, used as an acceptance test for the design of the publicly intended Number alias template, will the function always return true?
template <int N>
bool wrappedValueIsN(const Number<N>& num) {
// Always 'true', by design of the 'NumberImpl' class?
return N == num.value;
}
We will answer this question assuming that no user abuses the interface by specializing the semantically hidden detail::NumberImpl, in which case the answer is:
C++11: Yes
C++14: No
C++17: No
C++20: Yes
The key difference is that the class template detail::NumberImpl (for any non-explicit specialization of it) is an aggregate in C++14 and C++17, whereas it is not an aggregate in C++11 and C++20. As covered above, initialization of an object using direct-list-init or copy-list-init will result in aggregate initialization if the object is of an aggregate type. Thus, what may look like value-initialization (e.g. Number<1> n{} here)—which we may expect will have the effect of zero-initialization followed by default-initialization as a user-declared but not user-provided default constructer exists—or direct-initialization (e.g. Number<1>n{2} here) of a class type object will actually bypass any constructors, even deleted ones, if the class type is an aggregate.
struct NonConstructible {
NonConstructible() = delete;
NonConstructible(const NonConstructible&) = delete;
NonConstructible(NonConstructible&&) = delete;
};
int main() {
//NonConstructible nc; // error: call to deleted constructor
// Aggregate initialization (and thus accepted) in
// C++11, C++14 and C++17.
// Rejected in C++20 (error: call to deleted constructor).
NonConstructible nc{};
}
Thus, we can fail the wrappedValueIsN acceptance test in C++14 and C++17 by bypassing the private and deleted user-declared constructors of detail::NumberImpl by means of aggregate initialization, specifically where we explicitly provide a value for the single value member thus overriding the designated member initializer (... value{N};) that otherwise sets its value to N.
constexpr bool expected_result{true};
const bool actual_result =
wrappedValueIsN(Number<42>{41}); // false
// ^^^^ aggr. init. int C++14 and C++17.
Note that even if detail::NumberImpl were to declare a private and explicitly defaulted destructor (~NumberImpl() = default; with private access specifyer) we could still, at the cost of a memory leak, break the acceptance test by e.g. dynamically allocating (and never deleting) a detail::NumberImpl object using aggregate initialization (wrappedValueIsN(*(new Number<42>{41}))).
But why is detail::NumberImpl an aggregate in C++14 and C++17, and why is it not an aggregate in C++11 and C++20? We shall turn to the relevant standard passages for the different standard versions for an answer.
Aggregates in C++11
The rules governing whether a class is an aggregate or not is covered by [dcl.init.aggr]/1, where we refer to N3337 (C++11 + editorial fixes) for C++11 [emphasis mine]:
An aggregate is an array or a class (Clause [class]) with no
user-provided constructors ([class.ctor]), no
brace-or-equal-initializers for non-static data members
([class.mem]), no private or protected non-static data members (Clause
[class.access]), no base classes (Clause [class.derived]), and no
virtual functions ([class.virtual]).
The emphasized segments are the most relevant ones for the context of this answer.
User-provided functions
The detail::NumberImpl class does declare four constructors, such that it has four user-declared constructors, but it does not provide definitions for any of these constructors; it makes use of explicitly-defaulted and explicitly-deleted function definitions at the constructors’ first declarations, using the default and delete keywords, respectively.
As governed by [dcl.fct.def.default]/4, defining an explicitly-defaulted or explicitly-deleted function at its first declaration does not count as the function being user-provided [extract, emphasis mine]:
[…] A special member function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. […]
Thus, the detail::NumberImpl fulfills the aggregate class requirement regarding having no user-provided constructors.
For the some additional aggregate confusion (which applies in C++11 through C++17), where the explicitly-defaulted definition is provided out-of-line, refer to my other answer here.
Designated member initializers
Albeit the detail::NumberImpl class has no user-provided constructors, it does use a brace-or-equal-initializer (commmonly referred to as a designated member initializer) for the single non-static data member value. This is the sole reason as to why the detail::NumberImpl class is not an aggregate in C++11.
Aggregates in C++14
For C++14, we once again turn to [dcl.init.aggr]/1, now referring to N4140 (C++14 + editorial fixes), which is nearly identical to the corresponding paragraph in C++11, except that the segment regarding brace-or-equal-initializers has been removed [emphasis mine]:
An aggregate is an array or a class (Clause [class]) with no
user-provided constructors ([class.ctor]), no private or protected
non-static data members (Clause [class.access]), no base classes
(Clause [class.derived]), and no virtual functions ([class.virtual]).
Thus, the detail::NumberImpl class fulfills the rules for it to be an aggregate in C++14, thus allowing circumventing all private, defaulted or deleted user-declared constructors by means of aggregate initialization.
We will get back to the consistently emphasized segment regarding user-provided constructors once we reach C++20 in a minute, but we shall first visit some explicit puzzlement in C++17.
Aggregates in C++17
True to its form, the aggregate once again changed in C++17, now allowing an aggregate to derive publicly from a base class, with some restrictions, as well as prohibiting explicit constructors for aggregates. [dcl.init.aggr]/1 from N4659 ((March 2017 post-Kona working draft/C++17 DIS), states [emphasis mine]:
An aggregate is an array or a class with
(1.1) no user-provided, explicit, or inherited constructors ([class.ctor]),
(1.2) no private or protected non-static data members (Clause [class.access]),
(1.3) no virtual functions, and
(1.4) no virtual, private, or protected base classes ([class.mi]).
The segment in about explicit is interesting in the context of this post, as we may further increase the aggregate cross-standard-releases volatility by changing the declaration of the private user-declared explicitly-defaulted default constructor of detail::NumberImpl from:
template <int N>
struct NumberImpl final {
// ...
private:
NumberImpl() = default;
// ...
};
to
template <int N>
struct NumberImpl final {
// ...
private:
explicit NumberImpl() = default;
// ...
};
with the effect that detail::NumberImpl is no longer an aggregate in C++17, whilst still being an aggregate in C++14. Denote this example as (*). Apart from copy-list-initialization with an empty braced-init-list (see more details in my other answer here):
struct Foo {
virtual void fooIsNeverAnAggregate() const {};
explicit Foo() {}
};
void foo(Foo) {}
int main() {
Foo f1{}; // OK: direct-list-initialization
// Error: converting to 'Foo' from initializer
// list would use explicit constructor 'Foo::Foo()'
Foo f2 = {};
foo({});
}
the case shown in (*) is the only situation where explicit actually has an effect on a default constructor with no parameters.
Aggregates in C++20
As of C++20, particularly due to the implementation of P1008R1 (Prohibit aggregates with user-declared constructors) most of the frequently surprising aggregate behaviour covered above has been addressed, specifically by no longer allowing aggregates to have user-declared constructors, a stricter requirement for a class to be an aggregate than just prohibiting user-provided constructors. We once again turn to [dcl.init.aggr]/1, now referring to N4861 (March 2020 post-Prague working draft/C++20 DIS), which states [emphasis mine]:
An aggregate is an array or a class ([class]) with
(1.1) no user-declared, or inherited constructors ([class.ctor]),
(1.2) no private or protected non-static data members ([class.access]),
(1.3) no virtual functions ([class.virtual]), and
(1.4) no virtual, private, or protected base classes ([class.mi]).
We may also note that the segment about explicit constructors has been removed, now redundant as we cannot mark a constructor as explicit if we may not even declare it.
Avoiding aggregate surprises
All the examples above relied on class types with public non-static data members, which is commonly considered an anti-pattern for the design of “non-POD-like” classes. As a rule of thumb, if you’d like to avoid designing a class that is unintentionally an aggregate, simply make sure that at least one (typically even all) of its non-static data members is private (/protected). For cases where this for some reason cannot be applied, and where you still don’t want the class to be an aggregate, make sure to turn to the relevant rules for the respective standard (as listed above) to avoid writing a class that is not portable w.r.t. being an aggregate or not over different C++ standard versions.
Actually, MSDN addressed your concern in the below document:
Modified specification of aggregate type
In Visual Studio 2019, under /std:c++latest, a class with any user-declared constructor (for example, including a constructor declared = default or = delete) isn't an aggregate. Previously, only user-provided constructors would disqualify a class from being an aggregate. This change puts additional restrictions on how such types can be initialized.

Does it violate the standard for a non-default-constuctible struct to lack a user-defined constructor?

It is possible to define a struct (a) that has no user-defined constructors, and (b) for which a default constructor cannot be generated. For example, Foo in the below:
struct Baz
{
Baz(int) {}
};
struct Foo
{
int bar;
Baz baz;
};
You can still create instances of Foo using aggregate initialization:
Foo foo = { 0, Baz(0) };
My normal compiler (VS2012) will grudgingly accept this, but it raises 2 warnings:
warning C4510: 'Foo': default constructor could not be generated.
warning C4610: struct 'Foo' can never be instantiated - user defined constructor required
Of course, I've just proved warning #2 wrong--you can still instantiate it using aggregate initialization. The online compilers I've tried are happy enough to accept the above, so I'm guessing VS2012 is just being overly-aggressive with this warning. But I'd like to be sure--is this code ok, or does it technically violate some obscure part of the standard?
The standard explicitly allows cases like Foo in [12.1p4]:
[...] If there is no user-declared constructor for
class X, a constructor having no parameters is implicitly declared as defaulted [...] A defaulted default constructor for class X is defined as
deleted if:
[...]
any potentially constructed subobject, except for a non-static data member with a brace-or-equal-initializer, has class type M (or array
thereof) and either M has no default constructor or overload
resolution (13.3) as applied to M’s default constructor results in an
ambiguity or in a function that is deleted or inaccessible from the
defaulted default constructor
[...]
Baz has no default constructor, so the emphasised part above applies (emphasis mine).
There's nothing 'undefined' or 'ill-formed' about such cases. The implicitly declared default constructor is defined as deleted, that's all. You could do the same thing explicitly, and it would still be just as valid.
The definition for aggregates is in [8.5.1p1]. For C++14, it is:
An aggregate is an array or a class (Clause 9) with no user-provided
constructors (12.1), no private or protected non-static data members
(Clause 11), no base classes (Clause 10), and no virtual functions
(10.3).
The 'no user-provided' part allows you to use = delete on all constructors that could possibly be implicitly declared (making them user-declared, but not user-provided) and the class would still be an aggregate, allowing you to use aggregate initialization on it.
As for warning C4610, I've encountered it before myself and reported it. As you can see, it's been fixed in the upcoming version of VC++.
It may be worth mentioning that the example I used in the bug report is taken directly from the standard, where it's treated as well-formed ([12.2p5.4]:
struct S { int mi; const std::pair<int,int>& mp; };
S a { 1, {2,3} };
This is similar to your case, but here, the implicitly declared default constructor is defined as deleted because the class has a non-static member of reference type that has no initializer.
Granted, it's only an example, but I think it's an additional indication that there's nothing wrong with these cases.
That's not actually aggregate initialization, it's uniform, which is only recently supported by VS. This warning is simply them not correctly updating it to reflect that that type can now be uniform initialized.
Aggregates may not have user-defined non-defaulted non-deleted constructors, and the rules for aggregate UDTs are that each member must also be an aggregate. Therefore, Baz is not an aggregate and as a direct result, neither can Foo be.

Understanding constructor concept

I don't understand what constructor means in C++ formally. I was reading 3.8 clause (Object lifetime, N3797) and come across with the following:
An object is said to have non-trivial initialization if it is of a
class or aggregate type and it or one of its members is initialized by
a constructor other than a trivial default constructor.
I would like to understand an initialization in general. I've read section 8.5, N3797. Is it true that if some object is initialized, a constructor (possibly trivial default) will be called? I mean that every initialization process (even zero-initialization) means constructor calling. It would be good if you provide corresponding references to the Standard.
I don't understand what contructor means in C++ formally.
As far as I know, the standard does not explicitly contain a definition of the term "constructor". However, §12.1/1 says
Constructors do not have names. A special declarator syntax is used to declare or define the constructor.
The syntax uses:
an optional decl-specifier-seq in which each decl-specifier is either a function-specifier or constexpr,
the constructor's class name, and
a parameter list
in that order. In such a declaration, optional parentheses around the constructor class name are ignored.
Thus, if you declare a member function according to this syntax, the function you are declaring is a constructor. In addition,
The default constructor (12.1), copy constructor and copy assignment operator (12.8), move constructor
and move assignment operator (12.8), and destructor (12.4) are special member functions. [ Note: The
implementation will implicitly declare these member functions for some class types when the program does
not explicitly declare them. The implementation will implicitly define them if they are odr-used (3.2).
See 12.1, 12.4 and 12.8. — end note ] Programs shall not define implicitly-declared special member functions.
(§12/1)
So there you go---every class has at least three constructors declared, whether implicitly or explicitly; and you can also declare other constructors using the syntax in §12.1/1. The entire set of functions thus declared forms the set of constructors.
Is it true that if some object is initialized, a constructor (possibly trivial default) will be called? I mean that every initialization process (even zero-initialization) means constructor calling.
No, this is false. For example, int has no constructors. This is despite the fact that you can initialize an int with similar syntax compared to initialization of objects of class type.
struct Foo {};
Foo f {}; // calls default constructor
int i {}; // sets the value of i to 0
Also, zero-initialization never invokes a constructor, but zero-initialization is also never the only step in the initialization of an object.
If by "object" you meant "object of class type", it is still not true that a constructor is always called, although three constructors are always declared, as stated above. See §8.5/7 on value initialization:
To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the
default constructor for T is called (and the initialization is ill-formed if T has no accessible default
constructor);
if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object
is zero-initialized and, if T's implicitly-declared default constructor is non-trivial, that constructor is
called.
if T is an array type, then each element is value-initialized;
otherwise, the object is zero-initialized.
Therefore, when the default constructor for a non-union class type is trivial and you are value-initializing an object of that type, the constructor really is not called.
Classes are types:
A class is a type.
§9 [class]
However, not all types are classes. The standard refers to types which are not class types (scalar types, for example) in §3.9.
Only class types, however, can have member functions:
Functions declared in the definition of a class, excluding those declared with a friend specifier, are called member functions of that class.
§9.3 [class.mfct]
Constructors are member functions. Therefore types without constructors can exist (i.e. types that are not class types). Therefore initialization does not necessarily involve calling a constructor, since non-class types (int for example) may be initialized.
Note that something does not have to be of class type to be an "object":
An object is a region of storage.
§1.8 [intro.object]
Therefore an int, while not being of class type, would be an "object".
I think your answer would be found in 3.6.2, and 8.5 of N3797.
A constructor is always called on object creation. But initialization of an object is
multi-step process.
I understand that Zero-Initialization is separate and performed as the first step during object initialization, the Constructor (possibly trivial default) is called later
A constructor is special function. It looks like a normal function. It doesn't have return type (though some say the return type is the object of the class) like void, int, char, double, etc. It has same name as name of Class. It runs automatically when object is created.
In C++, you don't need new operator to initialize an object. Just declare it and set its attribute. i.e. ClassA object1, object2;
For example
Class Player{
int jerseyNo;
//constructor
Player(){
cout<<"HELLO";
}
};
In main you can do the following:
Player nabin;
And you can declare destructor as well. Destructor is special function similar to constructor but runs when object is destroyed i.e. when object moves out of scope.
Example:
Class Player{
..
~Player(){
cout<<"Destructor ran";
}
..
};
P.S The order of execution of constructor and destructor is reverse.
Example:
Player p1,p2,p3;
The order of their execution
1. p1's constructor runs
2. p2's constructor runs
3. p3's constructor runs
4. p3's destructor runs
5. p2's destructor runs
6. p1's destructor runs
You asked:
Is it true that if some object is initialized, a constructor (possibly trivial default) will be called?
The short answer: No.
A longer answer: A constructor is called only for objects of class types. For objects of other types, there are no constructors, and hence constructors cannot be called.
You said:
I mean that every initialization process (even zero-initialization) means constructor calling.
Objects of class types can be initialized by calling constructors, which can be explicit or implicit. They can also be initialized by other initialization methods. Objects of other types are initialized by directly setting the initial values of memory occupied by them.
You have already the seen section 8.5 of the draft standard on initialization.
Details about class constructors can be found in section 12.1 Constructors.
Details about class initialization can be found in section 12.6 Initialization.
Basically, whenever you construct a data type in c++, it can happen in one of two ways. You can either be calling a constructor, or you can essentially be copying chunks of memory around. So constructors are not always called.
In C++, there is a notion of primitives. Integers, doubles, pointers, characters, pointers (to anything) are all primitives. Primitives are data types, but they are not classes. All primitives are safe to copy bitwise. When you create or assign primitives, no constructor is called. All that happens is that some assembly code is generated that copies around some bits.
With classes it's a little more complicated; the answer is that usually a constructor is called, but not always. In particular, C++11 has the concept of trivial classes. Trivial classes are classes that satisfy several conditions:
They use the defaults for all the 'special' functions: constructor, destructor, move, copy, assignment.
They don't have virtual functions.
All non static members of the class are trivial classes or primitives.
As far as C++11 is concerned, objects of any class that satisfy this requirement can be treated like chunks of data. When you create such an object, it's constructor will not be called. If you create such an object on the stack (without new), then its destructor will not be called at program termination either, as would normally be called.
Don't take my word for it though. Check out the assembly generated for the code I wrote. You can see that there are two classes, one is trivial and one is not. The non-trivial class has a call to its constructor in the assembly, the trivial one does not.

What is the exception specification for a defaulted virtual destructor in C++11?

Suppose I have:
class Foo
{
public:
virtual ~Foo()=default;
};
What is the exception-specification on the defaulted destructor? Is the defaulted destructor equivalent to:
virtual ~Foo() {};
or
virtual ~Foo() throw() {};
or
virtual ~Foo() noexcept {};
Section 15.4 of the C++11 standard says it depends on the exception specifications of the functions directly invoked by the destructor's implicit definition. In this case there are no members, and no base classes, so AFAIK there are no functions directly invoked by the implicit destructor. Is this an ambiguity (or omission) in the standard?
It matters, of course, because if it implicitly has throw(), then all subclasses must declare their destructors with throw(). Don't tell me it's a bad idea to throw exceptions in destructors, I know that. I deal with lots of legacy code where exception specs were not used at all.
As a point of information, when I tried:
class SubFoo : public Foo
{
public:
virtual ~SubFoo();
};
I got an error (mismatched exception specs) in GCC 4.4 (although I admit I may not have had the right command line switches), but not in XCode 4.3 using the "11" compilers.
Back up to earlier in the same sentence (§15.4/14):
...its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition;..."
Therefore, if ~Foo doesn't invoke any functions, it has an implicit declaration that allows no exceptions to be thrown.
According to §15.4/3:
Two exception-specifications are compatible if:
both are non-throwing (see below), regardless of their form,
That's the case here, so it doesn't really matter whether the declaration is throw() or noexcept -- the two are compatible in any case.
The standardese starts nicely in C++11 §8.4.2/2,
If a function is explicitly defaulted on its first declaration,
— it is implicitly considered to be constexpr if the implicit declaration would be,
— it is implicitly considered to have the same exception-specification as if it had been implicitly declared (15.4), …
But then, over in C++11 §15.4/14, the logic rapidly devolves,
An implicitly declared special member function (Clause 12) shall have an exception-specification. If f is an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only
if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition; f shall allow all exceptions if any function it directly invokes allows all exceptions, and f shall allow no exceptions if every function it directly invokes allows no exceptions.
In the standard's meaning of "allow" it is about explicitly allowing, through an exception specification.
If f calls two functions, one of which specifies and therefore allows T, and one of which allows all exceptions, then f must both specify T and allow all exceptions, which isn’t possible.
So this definitely looks like a defect in the standard.
I found a related Defect Report, http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1351.
However, it looks like this area is just a Big Mess. :-(