This question already has answers here:
What are the advantages of list initialization (using curly braces)?
(5 answers)
Closed 3 years ago.
In my header, I have these two public variables:
float testVariable1 = 1.23456789f;
float testVariable2{ 1.23456789f };
What's the difference here?
Why have the version with the braces?
C++ supports three basic ways to initialize a variable.
First, we can do copy initialization by using an equals sign:
int width = 5; // copy initialization of value 5 into variable width
This copies the value on the right-hand side of the equals to the variable being created on the left-hand side.
Second, we can do a direct initialization by using parenthesis.
int width( 5 ); // direct initialization of value 5 into variable width
For simple data types (like integers), copy and direct initialization are essentially the same. But for some advanced types, direct initialization can perform better than copy initialization.
Before C++11, direct initialization was recommended over copy initialization in most cases because of the performance boost.
Unfortunately, direct initialization can’t be used for all types of initialization. In an attempt to provide a more consistent initialization mechanism, C++11 added a new syntax for direct initialization called brace initialization (also called uniform initialization) that uses curly braces:
int width{ 5 }; // brace (uniform) initialization of value 5 into variable width
Hope this will help you.
They have the same effect for this case.
The 1st one is copy initialization,
Otherwise (if neither T nor the type of other are class types), standard conversions are used, if necessary, to convert the value of other to the cv-unqualified version of T.
the 2nd one is direct-list-initialization.
Otherwise (if T is not a class type), if the braced-init-list has only one element and either T isn't a reference type or is a reference type whose referenced type is same as or is a base class of the type of the element, T is direct-initialized (in direct-list-initialization) or copy-initialized (in copy-list-initialization), except that narrowing conversions are not allowed.
and
Otherwise, standard conversions are used, if necessary, to convert the value of other to the cv-unqualified version of T, and the initial value of the object being initialized is the (possibly converted) value.
As build-in types, one of the potential differences is that narrowing conversions are not allowed in list initialization. (Even it won't be applied in this case because the initializers have been specified as float literals.)
Related
I'm using the clang compiler (c++ 11 I think) that comes with RAD studio 10.2. By mistake I discovered today that the first n members of a struct or array can be assigned using the usual curly brackets e.g.
int a[500]={1};
struct {int a,b,c;} st={2,3};
The above compiles and works fine but I've never come across this or seen it used before and I can find no mention of it online (maybe I'm searching using the wrong type of wording). Is this c++ documented?
For aggregate initialization,
(emphasis mine)
If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default member initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates).
That means, for int a[500]={1};, the 1st element is initialized to 1 and the remaining 499 elements of the array are value-initialized to 0. For struct {int a,b,c;} st={2,3};, the member a is initialized to 2 and b is initialized to 3, the last member c is value-initialized to 0 too.
I've never come across this or seen it used before and I can find no mention of it online (maybe I'm searching using the wrong type of wording). Is this c++ documented?
Yes, this is documented. This syntax is called list initialisation - and more specifically since the types are aggregates: This is aggregate initialisation.
Initialise first n members only of struct or array
It is not possible to only initialise some members / elements. If you list initialise class object or array, then all of it will be initialised. The members / elements which lack an initialiser will be value initialised.
If you wanted to do that, then what you can do instead is default initialise the class object or array, and then initialise the sub objects selectively afterwards.
I came upon this interesting answer when our team was dealing with a valgrind warning about unitialized members of a POD in our C++ code:
https://stackoverflow.com/a/5914697/629530
Restating the salient points, consider the following POD structure in C++:
struct C
{
int x;
int y;
};
The following invocations of constructing an object of type C invokes the default constructor and the members are initialized with that default constructor (again, copying the code and comments from Martin York's answer):
C c = C(); // Zero initialize using default constructor
C c{}; // Latest versions accept this syntax.
C* c = new C(); // Zero initialize a dynamically allocated object.
Makes sense. Martin York goes on to point out that the with the following declarations, however, the members of c are not initialized via a constructor and therefore contain undefined data:
C c; // members are random
C* c = new C; // members are random (more officially undefined).
That's interesting. I had used braced-init-list initialization of POD types before, but I didn't realize that C c; would not call the default constructor for a POD type. His answer satisfies the question, but I'd like to know specifically what is instantiated when the latter, non-default constructed c objects are declared. Specifically, the following would be helpful to me:
What is the official name for this non-default initialization of POD types? I'm having trouble googling for this mechanism because I don't know its name.
If the POD type has something less trivial than an int-type, such as a std::string, is the memory for that member also initialized with undefined values? Or is the default constructor for a std::string called for that member?
Update.
Thanks for the input. This has been duplicated to a question with this single answer:
https://stackoverflow.com/a/8860787/629530
According to that answer (and the answers to the question it is duplicated to), a declaration of the form without a parentheses is called "default initialized":
Info *p = new Info; <------- Default Initialization
For default initialization, these points about initialization are made:
To default-initialize an object of type T means:
if T is a non-POD class type (clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
if T is an array type, each element is default-initialized;
otherwise, the object is zero-initialized.
I might be misunderstanding something, but the struct named C proposed above is a POD type that is not an array. Thus it should be zero initialized? The members are not, however, zero initialized but contain undefined values. How do I reconcile this?
default-initialize an object of type T means:
... otherwise, the object is zero-initialized.
No.
Your first linked answer is correct about C++11 onwards, and this is called default-initialization. It's different to default-construction or value initialization.
The second linked answer was probably correct for C++03, but is wrong for C++11 onwards (even though it was written in 2012). I don't have a copy of the '03 standard to verify, and it was a long time ago.
The effects of default initialization are:
if T is a non-POD (until C++11) class type, the constructors are considered and subjected to overload resolution against the empty argument list. The constructor selected (which is one of the default constructors) is called to provide the initial value for the new object;
if T is an array type, every element of the array is default-initialized;
otherwise, nothing is done: the objects with automatic storage duration (and their subobjects) are initialized to indeterminate values.
Local copy of N4659 agrees with the summary above:
11.6 Initializers [dcl.init]
...
(7.3) Otherwise, no initialization is performed
The section on new-expressions even refers to 11.6 and then says
Note: If no initialization is performed, the object has an indeterminate value.— end note
Current draft has
9.3 Initializers [dcl.init]
...
(7.3) Otherwise, no initialization is performed.
This question is regarding std::initializer_list, and why it is allowed to initialise primitive types. Consider the following two functions:
void foo(std::string arg1, bool arg2 = false);
void foo(std::string arg1, std::deque<std::string> arg2, bool arg3 = false);
Why is it that, when calling foo like this:
foo("some string", { });
The first overload is picked, instead of the second? Well, actually not why it's picked, it's because { } can be used to initialise anything, including primitive types. My question is the reasoning behind this.
std::initializer_list takes { args... }, and as such cannot have indeterminate length at the time of compilation. Attempting to do something like bool b = { true, true } gives error: scalar object 'b' requires one element in initialiser.
While it might have seemed like a good idea to allow uniform initialisation, the fact is that this is confusing and entirely unexpected behaviour. Indeed, how is the compiler able to do this, without some magic in the background doing std::initializer_list things?
Unless { args... } is a C++ lexical construct, in which case my point still stands: why is it allowed to be used in the initialisation of primitive types?
Thanks. I had quite the bug-hunting session here, before realising that the wrong overload was being called. Spent 10 minutes figuring out why.
That {} syntax is a braced-init-list, and since it is used as an argument in a function call, it copy-list-initializes a corresponding parameter.
§ 8.5 [dcl.init]/p17:
(17.1) — If the initializer is a (non-parenthesized) braced-init-list, the object or reference is list-initialized (8.5.4).
§ 8.5.4 [dcl.init.list]/p1:
List-initialization is initialization of an object or reference from a braced-init-list. Such an initializer is
called an initializer list, and the comma-separated initializer-clauses of the list are called the elements of the
initializer list. An initializer list may be empty. List-initialization can occur in direct-initialization or copy-initialization contexts; [...]
For a class-type parameter, with list-initialization, overload resolution looks up for a viable constructor in two phases:
§ 13.3.1.7 [over.match.list]/p1:
When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor
in two phases:
— Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of the initializer list as a single argument.
— If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.
but:
If the initializer list has no elements and T has a default constructor, the first phase is omitted.
Since std::deque<T> defines a non-explicit default constructor, one is added to a set of viable functions for overload resolution. Initialization through a constructor is classified as a user-defined conversion (§ 13.3.3.1.5 [over.ics.list]/p4):
Otherwise, if the parameter is a non-aggregate class X and overload resolution per 13.3.1.7 chooses a single
best constructor of X to perform the initialization of an object of type X from the argument initializer list,
the implicit conversion sequence is a user-defined conversion sequence with the second standard conversion
sequence an identity conversion.
Going further, an empty braced-init-list can value-initialize its corresponding parameter (§ 8.5.4 [dcl.init.list]/p3), which for literal types stands for zero-initialization:
(3.7) — Otherwise, if the initializer list has no elements, the object is value-initialized.
This, for literal types like bool, doesn't require any conversion and is classified as a standard conversion (§ 13.3.3.1.5 [over.ics.list]/p7):
Otherwise, if the parameter type is not a class:
(7.2) — if the initializer list has no elements, the implicit conversion sequence is the identity conversion.
[ Example:
void f(int);
f( { } );
// OK: identity conversion
— end example ]
Overload resolution checks in first place if there exists an argument for which a conversion sequence to a corresponding parameter is better than in another overload (§ 13.3.3 [over.match.best]/p1):
[...] Given these definitions, a viable function F1 is defined to be a better function than another viable function
F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then:
(1.3) — for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that, [...]
Conversion sequences are ranked as per § 13.3.3.2 [over.ics.rank]/p2:
When comparing the basic forms of implicit conversion sequences (as defined in 13.3.3.1)
(2.1) — a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and [...]
As such, the first overload with bool initialized with {} is considered as a better match.
Unfortunately, {} does not actually indicate an std::initializer_list. It is also used for uniform initialization. Uniform initialization was intended to fix the problems of the piles of different ways C++ objects could be initialized but ended up just making things worse, and the syntactic conflict with std::initializer_list is fairly awful.
Bottom line is that {} to denote an std::initializer_list and {} to denote uniform initialization are two different things, except when they're not.
Indeed, how is the compiler able to do this, without some magic in the
background doing std::initialiser_list things?
The aforementioned magic most assuredly exists. { args... } is simply a lexical construct and the semantic interpretation depends on context- it is certainly not an std::initializer_list, unless the context says it is.
why is it allowed to be used in the initialisation of primitive types?
Because the Standards Committee did not properly consider how broken it was to use the same syntax for both features.
Ultimately, uniform init is broken by design, and should realistically be banned.
My question is the reasoning behind this.
The reasoning behind it is simple (albeit flawed). List-initialization initializes everything.
In particular, {} stands for "default" initializing the object it corresponds to; Whether this means that its initializer_list-constructor is called with an empty list, or that its default constructor is called, or that it is value-initialized, or that all of an aggregates subobjects are initialized with {}, etc. is irrelevant: It is supposed to act as a universal initializer for any object that the above can be applied to.
If you wanted to call the second overload, you'd have to pass e.g. std::deque<std::string>{} (or pass three arguments in the first place). That is the current modus operandi.
While it might have seemed like a good idea to allow uniform
initialisation, the fact is that this is confusing and entirely
unexpected behaviour.
I wouldn't call it "entirely unexpected" by any means. What is confusing about list-initializing primitive types? It is absolutely vital for aggregates - but there's not that big of a step from aggregate types to arithmetic ones, as no initializer_list is involved in both cases. Don't forget that it can e.g. be useful to prevent narrowing as well.
std::initialiser_list takes { args... }, and as such cannot have
indeterminate length at the time of compilation.
Well, technically speaking,
std::initializer_list<int> f(bool b) {
return b? std::initializer_list<int>{} : std::initializer_list<int>{1};
}
Consider following program.
#include <iostream>
int main()
{
int a=int{};
std::cout<<a;
}
Is it uses aggregate initialization or default initialization? I am confused.
Empty parentheses or braces (T() or T{}) perform value initialization. The exception would be the case where the type is an aggregate in which case aggregate initialization would be used. Since int is not an aggregate, it will be value initialized and since it's not a class nor an array, value initialization will do zero-initialization.
You were wondering why it doesn't work in C. Such syntax simply doesn't exist in C, see this answer.
Aggregate initialization is a type of list initialization, which initializes aggregates. An aggregate is an object of type array, or object which has the characteristics defined on this page.
In this case, the type of initialization is most likely value initialization.
Since C++11, by comparison with other SO answers (e.g.: this or this), I would say this is:
a value-initialization (int{}) followed by
a copy-initialization (int a=int{}).
By the way, from C++17, the second step should vanish as int{} is required to directly initialize a.
This question already has an answer here:
Can I Reference Previous Members of an Initializer List?
(1 answer)
Closed 11 months ago.
I was wondering about an initialization of the following form:
int array[] = {
v - 1,
array[0] + 1
} ;
In the initialization of the second element, the value of the first is used, but the entire array is not yet initialized. This happens to compile with g++, but I was unsure whether this is actually portable and a well defined construct?
See 3.3.2 Point of declaration:
The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its
initializer (if any), except as noted below. [ Example:
int x = 12;
{ int x = x; }
Here the second x is initialized with its own (indeterminate) value. —end example ]
So you are referring to the array correctly, its name is known after the =.
Then, 8.5.1 Aggregates:
An aggregate is an array or a class [...]
17: The full-expressions in an initializer-clause are evaluated in the order in which they appear.
However, I see no reference to when the evaluated values are actually written into the array, so I wouldn't rely on this and would even go so far to declare your code as not well defined.
As far as I can see, this is not well defined. The standard (C++11, 8.5.1/17) specifies that "The full-expressions in an initializer-clause are evaluated in the order in which they appear", but I can't see anything that requires each aggregate element to be initialised from the result of its initializer-clause before the next is evaluated.
Can a (C/C++) array initialization reference itself?
This is also valid C code.
C has some correspondent paragraph (emphasis mine).
(C99, 6.2.1p7) "Structure, union, and enumeration tags have scope that begins just after the appearance of the tag in a type specifier that declares the tag. Each enumeration constant has scope that begins just after the appearance of its defining enumerator in an enumerator list. Any other identifier has scope that begins just after the completion of its declarator."
I think this is handled by http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1343 . Initially my report was only about non-class initializers for namespace scope objects (see When exactly is an initializer temporary destroyed?), but the problem exists for aggregate elements just aswell if they are non-class. And as the additional recent note explains, even seems to exist for the entire aggregate initialization aswell, even if it is a class object, because then no constructor call happens that would enlargen the full-expression of the initializer.
If instead of int you would have used a class, and the initialization would be a constructor call, then that constructor call would be part of the same full expression that encloses the aggregate-ininitializer element, so that here the order would be OK and your code would be well-defined.