Order of evaluation - c++

I wonder if construction like this (initialization list) has well defined EO (evaluation order):
struct MemoryManager
{
Pair* firstPair_;//<-beg
Pair* currentPair_;
Pair* lastPair_;//<-end
MemoryManager():lastPair_(currentPair_ = firstPair_ = nullptr)
{/*e.b.*/}
};
If yes I personally would prefer this way to the more conventional:
MemoryManager():firstPair_(nullptr),
currentPair_(nullptr),
lastPair_(nullptr)
{/*e.b*/}

As John Dibling has remarked, your construct is technically correct for the given concrete example, but it's brittle and it's hard to understand for many programmers.
Brittleness:
Can fail if order of declaration is changed.
Can fail if the init list is changed.
To evaluate such constructs on your own, keep this idea foremost in your mind: code is not about instructing the compiler to do your bidding, it is about communicating your intent to others (and perhaps your later self).
Hence, try to write clear code.
Cheers & hth.,

Yes. As shown in your code, the members will be initialized in the same order they are declared in the struct/class definition (the order of initializers in the constructor definition is irrelevant, at best you will get a warning telling you they are in an incorrect order).
12.6.2 §5: Then, nonstatic data members shall be initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
Note that this is only true for variables that are part of the same access specifier, so for instance variables found in a public: specifier may be initialized before or after those found in a private: specifier (the struct counts as a public: specifier, of course).
EDIT: the above paragraph was incorrect, I was thinking of allocation, not initialization:
9.2 §12: Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allocated so that later members have higher addresses within a class object. The order of allocation of nonstatic data members separated by an access-specifier is unspecified (class.access.spec).
However, the more conventional way has a reason to exist, namely that if the order of declaration of the variables changes (for instance, due to refactoring), the code will not break silently. People don't readily assume the order of declaration is relevant, unless a warning tells them otherwise.

If you want to do this, then do it the way everybody understands immediately without having to browse the standard:
MemoryManager()
// no initialization here
{
lastPair_ = currentPair_ = firstPair_ = nullptr;
}
However, I don't really see what this buys you over
MemoryManager()
: lastPair_(), currentPair_(), firstPair_()
{}
which does exactly the same in only about half a dozen more characters.

For this specific example, the initialization order for members is irrelevant. The following constructor would have the same behaviour as the one in the question:
MemoryManager():firstPair_(lastPair_ = currentPair_ = nullptr)
{/*e.b.*/}
This is because the members are POD and so are not default initialized by the constructor at all (12.6.2/4 C++ '03):
If a given nonstatic data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then
If the entity is a nonstatic data member of (possibly cv-qualified) class type (or array thereof) or a base class, and the entity class is a non-POD class, the entity is default-initialized (8.5). If the entity is a non- static data member of a const-qualified type, the entity class shall have a user-declared default constructor.
Otherwise, the entity is not initialized. If the entity is of const-qualified type or reference type, or of a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of a const-qualified type, the program is ill-formed.
For the raw pointer members above, the 'otherwise' bullet applies.
Now, even if the members did have class type, say:
class MbrType {
public:
MbrType();
MbrType(int *);
MbrType(MbrType const &);
MbrType & operator=(MbrType const &);
};
Then, the constructor as you've written it would result in the members having the values that you expect, but a non optimizing compiler would be allowed to implement your constructor as:
MemoryManager()
: firstPair_ () // implicit call to default constructor
, currentPair_ () // implicit call to default constructor
, lastPair_(currentPair_.operator=(firstPair_.operator=(MbrType (nullptr))))
{/*e.b.*/}
Resulting in 6 calls rather than 3.

No, but it doesn't matter. Your code does not depend on the order in which currentPair_ and firstPair_ are zeroed.

Related

Can const-default-constructible objects be of non-class types?

Per my understanding, for class type T to be const-default-constructible type, the default-initialization of T shall invoke a user-provided constructor, or T shall provide a default member initializer for each non-variant non-static data member: ([dcl.init]/7)
A class type T is const-default-constructible if
default-initialization of T would invoke a user-provided constructor
of T (not inherited from a base class) or if
(7.4) each direct
non-variant non-static data member M of T has a default member
initializer [..]
Noting the bold part, it seems to me that const-default-constructible types can only be class types (including unions and structs). Therefore I can't say that const int, for example, is const-default-constructible type since int is not a class type.
You might ask, from where this confusion comes. Basically, [class.default.ctor]/2 says:
A defaulted default constructor for class X is defined as deleted
if:
[..]
(2.4) any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer is not
const-default-constructible ([dcl.init]),
[..]
Notice the word "any". This bullet considers cases where X has data member M, and M is of class types as well as non-class types. But per [dcl.init]/7, the const-default-constructible types are limited to only be class types.
Consider the following example,
struct S
{
const int I;
// Is the type of S::I said to be non-const-default-constructible?
S() = default;
};
Is there missing wording? Am I misreading the quotes?
PS: Thanks to #463035818_is_not_a_number for clarifying this. Consider this case:
struct X
{
const int M = 0;
// Is the type of X::M said to be const-default-constructible?
X() = default;
};
Is the type of S::I said to be non-const-default-constructible?
Yes. Like you've quoted in [dcl.init]/7 only class types can be const-default-constructible. The reason for this is non-class types do not have a default constructor, meaning they have no default value that can be used if they are declared like
const T foo;
When you read
any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer is not const-default-constructible ([dcl.init])
It is saying that if you have a member in the form of const T name; in your class then the default constructor is deleted if T is not const-default-constructible. In your case that means your constructor is deleted because const int is not const-default-constructible.
In the case of X, M is still not const-default-constructible because it is not a class type. X though is const-default-constructible because M has a brace-or-equal-initializer so [class.default.ctor]/2.4 does not apply.
This can be boiled down into a simple rule: All const objects must have an initializer
Since built in types do not get default initialized they must have a value provided by the programmer.
Non-class types are never const-default-constructible as you have read correctly.
But I don't think there is any wording missing. It seems intentional. The whole point of const-default-constructible is to make sure that you can't accidentally leave an object with an indeterminate value after initialization when you can't change that object's value later.
It would be UB to try to change the value of I after initialization (because it is declared const), but with the defaulted default constructor or without an explicit initializer on I, it would be default-initialized to an indeterminate value (because that is the effect of default-initialization on non-class types) which then is impossible to change. An indeterminate value can be used for almost nothing. The standard doesn't even allow copying it (except for types unsigned char and std::byte).
So there is no reason to allow you to default-construct this S with the defaulted default constructor.
(Technically the standard now allows using placement-new to replace the I object transparently as long as it is not part of a const-complete object, which allows changing its value in some sense, but that shouldn't be a normal use case.)

How to understand the concept of variable

The concept of the variable is defined as:
A variable is introduced by the declaration of a reference other than a non-static data member or of an object. The variable's name, if any, denotes the reference or object.
The relevant concept of the declaration of an object is defined as:
dcl.dcl#dcl.pre-10
If the decl-specifier-seq contains no typedef specifier, the declaration is called a function declaration if the type associated with the name is a function type ([dcl.fct]) and an object declaration otherwise.
So, consider the below code, which entities are called variable?
class A{
int a; // #1 `a` can be called a variable?
int& rf; // #2
};
void fun(int p, int& prf){ // #3 where p can be called a variable, and how about `prf`
}
int main(){
int data = 0; // data is absolutely called a variable
int& ref = data;// ref is absolutely called a variable
}
I wonder whether #1 can be called a variable? Sometimes, the name a is introduced by member-declaration. Hence, it's a member of class A. But sometimes, it is also called member subobject. According to the definition of terminology variable, it's introduced by a declaration of an object.
So, can name a be called variable? Is it call a member or a member subobject? If it is the latter, I think it should be called a variable. Moreover, its associated type is not a function type.
About #2, it's just a data member of class A and is definitely not a variable(it's a non-static data member of class A of reference type).
Are parameters int p and int& prf in the declaration of fun called variable? According to [dcl.dcl#dcl.pre-10], their associated types are all not function types, so they should be declarations of objects. Namely, they should be called variables, Right?
Whether the rule [dcl.dcl#dcl.pre-10] still applies to parameter declaration and member-declaration?
Moreover, why the definition for variable only qualifies reference type that it shall not be a reference of a non-static member of a class? why not give a similar qualification for an object? Maybe a non-static data member other than a reference type is not an object at all? If it was, when will we call a a member subobject?
Please interpret these issues.
The standard is generally confused about the difference between a variable (a kind of entity that can, for instance, be found by name lookup) and an object (a unit of data that exists for some portion of the program’s execution). A non-reference variable with static storage duration has exactly one object associated with it—not counting tricks like explicit destructor calls and placement new—but a thread-local variable can have more than one object associated with it, and a (non-reference) variable with automatic storage duration has zero or more based on how many times its function or block has been reentered (on however many threads) at any moment (plus any number of suspended coroutine frames as of C++20). Non-static data members (again, that are not references) are even more arbitrary: there is an object for each for each object of the containing type, including all the cases above as well as those with dynamic storage duration. (For variables and non-static data members of reference type, all the above holds mutatis mutandis, but we don’t have a good, separate term for “an instance of a reference-type variable”.)
The situation is even worse with base classes: if D inherits from B and C which each inherit from A, D has three base classes but each D object has four base class subobjects. We don’t have a good term for “appearance of a class in the inheritance lattice of another” to capture that four-fold aspect without regard to a particular object, even though we need such a term for handling things like name lookup ambiguity.
Given the above, it’s hardly surprising that the answer to your question is going to be unsatisfactory, but here it is: a non-static data member (a “member variable”) is never a variable even though there can be many (sub)objects associated with it. It can be a reference, just like extern int &r; (which is also not an object), which is why the definition in [basic.pre] has to exclude non-static data members when it considers “references” (by which we mean certain entities) and “objects” (by which we mean certain other entities).

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.

Comparison of member initialization by constructor vs. direct initialization

I'm creating a class in which one member is a const pointer (immutable address) to another member of the struct.
In the simplified version below, will both classes always behave the same value? Especially in the sense of whether the addresses stored in ptr are guaranteed to be properly initialized.
struct First
{
int a;
int* const ptr = &a;
};
struct Second
{
int a;
int* const ptr;
Second() : ptr(&a) {}
};
(In my actual application the member a is a class instance, and ptr is replaced by a map from some enums to pointers pointing to members of a.)
In the simplified version below, will both structs always behave the same way?
No they won't, but it may be ok for your case. Read on.
Both First::ptr and Second::ptr will be initialized to the expected value being the address of First::a and respectively Second::a, but:
[class.mem]/7 & [class.mem]/9
7 In a member-declarator, an = immediately following the declarator is interpreted as introducing a pure-specifier
if the declarator-id has function type, otherwise it is interpreted as introducing a brace-or-equal-initializer.
9 A brace-or-equal-initializer shall appear only in the declaration of a data member. (For static data members,
see 12.2.3.2; for non-static data members, see 15.6.2 and 11.6.1). A brace-or-equal-initializer for a non-static
data member specifies a default member initializer for the member, and shall not directly or indirectly cause
the implicit definition of a defaulted default constructor for the enclosing class or the exception specification
of that constructor.
This means, First has a defaulted default constructor where Second has a user-provided default constructor, which change some characteristic of those classes. I can for instance think of aggregates, triviality and maybe standard layouts.

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.