In C and C++, one can initialize arrays and structs using braces:
int a[] = {2, 3, 5, 7};
entry e = {"answer", 42};
However, in a talk from 2007, Bjarne mentions that this syntax also works for scalars. I tried it:
int i = {7};
And it actually works! What is the rationale behind allowing the initialization of scalars with braces?
Note: I am specifically not talking about C++11 uniform initialization. This is good old C89 and C++98.
What is the rationale behind allowing the initialization of scalars with braces?
int is POD. So the brace initialization is allowed in case of int (and for all build-in types), as it makes the initialization-syntax consistent with other PODs.
Also, I guess whatever rationale behind C++11 Uniform Initialization Syntax are, are also (partially) applicable to this syntax allowed by C++03. It is just C++03 didn't extend this to include non-pod types such as the standard containers.
I can see one place where this initialization is helpful in C++03.
template<typename T>
void f()
{
T obj = { size() } ; //T is POD: built-in type or pod-struct
//code
}
Now this can be instantiated with struct which begins with a suitable member, as well as any arithmetic type:
struct header
{
size_t size; //it is the first member
//...
};
f<header>(); //body becomes : header obj = { size(); }; which is fine
f<size_t>(); //body becomes : size_t obj = { size(); }; which is fine
Also note that POD, whether struct or built-in types, can also be initialized uniformly as:
header h = header(); //value-initialized
int i = int(); //value-initialized
So I believe one reason is consistency!
The rationale is not mentioned, but from a 2005 C++ Standard draft, 8.5 Initializers [dcl.init], clause 14
If T is a scalar type, then a declaration of the form
T x = { a };
is equivalent to
T x = a;
Note that the C++ 98 Standard only allows brace initializers for copy-initialization T x = { a }, and not for direct initialization T x { a }, for which only T x(a) works.
UPDATE: see also this question
C++ probably inherited this from C. In C the main reason is to have a unique initilizer syntax, in particular for a default initializer. In C the default initializer is {0}.
Related
There have been other posts regarding initialization of array of nested structs. However, following the given advice elsewhere in Stackoverflow and links to Aggregate Initialization are not helping me resolve an issue.
My context is that I am initializing a number of nested structs in the Vulkan graphics API.
However, in looking at other codes for explanation I ran across the following 'C' initialization (please note that I am adding the defined structs and the code in question)
Vulkan typedefs
typedef union VkClearColorValue {
float float32[4];
int32_t int32[4];
uint32_t uint32[4];
} VkClearColorValue;
typedef struct VkClearDepthStencilValue {
float depth;
uint32_t stencil;
} VkClearDepthStencilValue;
typedef union VkClearValue {
VkClearColorValue color;
VkClearDepthStencilValue depthStencil;
} VkClearValue;
The 'C' code in question
const VkClearValue clear_values[2] = {
[0] = {.color.float32 = {0.2f, 0.2f, 0.2f, 0.2f}},
[1] = {.depthStencil = {demo->depthStencil, 0}},
};
The 'C' represenation is legal in C99 and the latest gcc compiler.
However, as I apply this to C++ I get errors.
Going back to rules for aggregate initialization the 'C' represenation should be legal in C++17. However, on compilation I get errors to the effect: error: expected primary-expression before '.' token
Clearly I am wrong in my assumption. As such, might anyone provide guidance on how to make it legal in C++17.
Going back to rules for aggregate initialization the 'C' represenation should be legal in C++17
You assume wrongly, as you have correctly concluded. C++ has had aggregate initialisation since the first standard version, but it doesn't support all initialisations that were added in C99. For example, designated initialisers are not in C++17 at all.
As such, trivial unions can only be initialised with the first member active, like in C89. To activate another member, it must be assigned after initialisation. To do this with a const object, you can use a function.
C++20 adds designated initialisers, but even then only subset of C is allowed. Following is well-formed in C++20:
const VkClearValue clear_values[2] = {
{.color {.float32 = {0.2f, 0.2f, 0.2f, 0.2f}}},
{.depthStencil = {42, 0}},
};
This is not a valid form of aggregate initialization in c++, even though it is in c. In fact, there is a note:
Note: out-of-order designated initialization, nested designated initialization, mixing of designated initializers and regular initializers, and designated initialization of arrays are all supported in the C programming language, but are not allowed in C++.
struct A { int x, y; };
struct B { struct A a; };
struct A a = {.y = 1, .x = 2}; // valid C, invalid C++ (out of order)
int arr[3] = {[1] = 5}; // valid C, invalid C++ (array)
struct B b = {.a.x = 0}; // valid C, invalid C++ (nested)
struct A a = {.x = 1, 2}; // valid C, invalid C++ (mixed)
Is it possible to use aggregate initialization to make a pointer aptr point to a which is a member of the same struct ?
struct S {
int a;
int* aptr;
};
int main() {
S s = {
.a = 3,
.aptr = &a //point aptr to a
};
return 0;
}
The question is for both C and C++.
A working initialization would be:
struct S {
int a;
int* aptr;
};
int main() {
struct S s = {.a = 3, .aptr = &s.a};
printf("%d", *s.aptr);
}
Working samples:
C11 GNU
C++2a GNU
Regarding the correctness of the initialization:
For C:
The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.
For C++:
Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions ([temp.variadic]), are evaluated in the order in which they appear.
That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list.
However, despite the differences we can observe, the order in which the expressions are evaluated does not seem matter in this case, since you're not actually accessing the value of s.a, just its address which is accessible at this point.
So this is a correct initialization both for C and C++.
Something to note with this code, in MSVC, there is a compilation error in C++:
use of designated initializers requires at least '/std:c++latest'
Using std:c++latest the error changes to:
designated and non-designated initializers is nonstandard in C++
However, compilers that range from clang 3.1 to clang 10.0 and gcc 4.9.0 to gcc 10.0 with C++03 to C++2a compile fine with no warnings.
Designated initializers where introduced in C++20, so it is actually correct not to accept them, as MSVC still does not accept /std:c++20, it is not possible to use them yet, it also looks like gcc and clang always provided support for these initializers.
That being said, a second solution would be:
struct S {
int a;
int* aptr;
};
int main() {
struct S s = { 3, &s.a };
printf("%d", *s.aptr);
}
This second version of initialization compiles with no issues in every compiler tested, so it's fair to assume that it is more portable.
The first version is probably more easily readable and allows for a easier identification of errors in initialization, one of the advantages of designated initializers.
This works:
int arr[10] = {};
All elements of arr are value-initialized to zero.
Why doesn't this work:
std::array<int, 10> arr({});
I get the following warning from g++ (version 4.8.2):
warning: missing initializer for member ‘std::array<int, 10ul>::_M_elems’
There are two issues one which is a matter of style and the warning.
Although it may not be obvious, aggregate initialization is happening on a temporary which is then being used as an argument to the copy constructor. The more idiomatic to do this initialization would be as follows:
std::array<int, 10> arr = {};
Although this still leaves the warning.
The warning is covered by gcc bug report: - -Wmissing-field-initializers relaxation request and one of the comments says:
[...]Surely, the C++ syntax of saying MyType x = {}; should be supported,
as seen here:
http://en.cppreference.com/w/cpp/language/aggregate_initialization
where for instance:
struct S {
int a;
float b;
std::string str;
};
S s = {}; // identical to S s = {0, 0.0, std::string};
That shouldn't warn for the reasons stated in earlier comments.
and a follow-up comment says:
My statement about zero-initialization was inaccurate (thanks), but
the general point still stands: in C you have to write ' = {0}' since
empty-braces initializer is not supported by the language (you get a
warning with -pedantic); in C++, you can write ' = {}' or 'T foo =
T();', but you don't need to write ' = {0}' specifically.
The latest versions of gcc does not produce this warning for this case, see it live working with gcc 5.1.
We can see this topic also covered in the Clang Developers lists in the thead: -Wmissing-field-initializers.
For reference the draft C++11 standard section 8.5.1 [dcl.init.aggr] says:
If there are fewer initializer-clauses in the list than there are
members in the aggregate, then each member not explicitly initialized
shall be initialized from an empty initializer list (8.5.4). [
Example:
struct S { int a; const char* b; int c; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of
an expression of the form int(), that is, 0. —end example ]
So as this is valid C++, although as noted using {} is not valid C99. One could argue that it is only a warning, but this seems like idiomatic C++ using {} for aggregate initialization and is problematic if we are using -Werror to turn warnings into errors.
Firstly, you can use the ({}) initializer with an std::array object, but semantically that stands for direct-initialization using copy-constructor from a temporary value-initialized std::array object, i.e. it is equivalent to
std::array<int, 10> arr(std::array<int, 10>{});
And it is actually supposed to compile.
Secondly, you don't really have to go the ({}) way when you can just do
std::array<int, 10> arr = {};
or
std::array<int, 10> arr{};
The first of the two is the most syntactically similar to your int arr[10] = {};, which makes me wonder why you didn't try it at first. Why did you decide to use ({}) instead of = {} when you built the std::array version of = {} syntax?
Enough people have pointed this out as a "problem" when compiling with -Werror that I think it's worth mentioning that the problem goes away if you just double up:
std::array<int, 10> arr{{}};
Does not produce any warning for me on gcc 4.9.2.
To add a bit as to why this solves it: my understanding was that std::array is actually a class with a C array as its only member. So double up on the braces makes sense: the outer braces indicate you are initializing the class, and then the inner braces default initialize the one and only member of the class.
Since there is no possible ambiguity when there is only one variable in a class, just using one pair of {} should be reasonable, but gcc is overly pedantic here, and warns.
I was watching MVA's(Microsoft Visual Academy's) tutorials and I came across these two operators i.e. {} and = to pass in the value to variables. I have done C programming so I am pretty much aware of the assignment operator =. But {} is not in any language I did so far.
Kate is teaching the C++ course so she told that {} is used for copying.
But I was using the operator {} in the class below and it shows some error when I try to do this:
this->_width{new_width};
whereas below one works:
this->_width = new_width;
Why so? I am also using {} to pass values in constructor but then they work perfectly. Only Problem is with member fucntions.
class Rectangle
{
public:
void resize(int new_height, int new_width) { this->_width{new_width ; this->_height{new_height}; //member function
Rectangle(): _width{} , height{} {} //constructor
private:
int _width;
int _height;
};
{} can be used to initialise variables in C++11 in the same way that they are used to initialise arrays and structures in C.
This has been introduced primarily to provide consistency in language syntax (initialising with {} will work in all contexts, whereas initialising using the assignment operator or () will work in specific contexts.
There is a further advantage to list initialisation in that it prevents narrowing - i.e. it prevents you from providing an integer when a double is required, or a short when an integer is required. In this way it can help to reduce bugs.
Note that {} cannot be used to pass values to a function - only to construct new objects.
This page is also worth reading
Using {} is called uniform initialization in this context. It was introduced mainly for two reasons.
First, as the name indicates, initialization is uniform, that is it looks and works the same for single objects, arrays, containers that accept initializer lists, etc.
Second, and equally important, it is impossible to get a most vexing parse using curly braces, which is quite possible unintentionally otherwise:
A a(); // What does this do? What was probably intended?
B b{}; // And what does this do?
Also, as a bonus (kudos to #Richard Hodges), you avoid narrowing conversions using uniform initialization.
To literally answer the question "which one should I use?", you should preferrably use {} as it has only advantages, and no disadvantages (plus, Bjarne Stroustrup recommends using it).
Non-static data members may be initialized in one of two ways:
1) In the member initializer list of the constructor.
struct S {
int n;
std::string s;
S() : n(7) // direct-initializes n, default-initializes s
{ }
};
2) Through a brace-or-equal initializer, which is simply an initializer included in the member declaration, which is used if the member is omitted in the member initializer list
struct S {
int n = 7;
std::string s{'a', 'b', 'c'};
S() // copy-initializes n, list-initializes s
{ }
};
You may use brace initializers in the member declaration.
Also, from the standard, N4296, § 9.2, paragraph 4:
A brace-or-equal-initializer shall appear only in the declaration of
a data member.
After reading up on list-initialization and its various flavours, I decided to test out some features in an openGL ES app I'm writing using Xcode and Objective-C++, until I ran into something rather obscure.
I've known about (and frequently implement) the conventional C style structure initialization using the following syntax to initialize POD, such as a GLKVector2, for example:
GLKVector2 point = { 0,0 }; //copy-initialized
but this approach may not always be what's intended by the programmer. So, by removing the assignment (and a needless copy construction operation), in favour of direct-initilization, one would assume (from the documentation) the above declaration would appear like so:
GLKVector2 point{ 0,0 }; //direct-initialized, but generates compile error
However, the compiler doesn't complain when the code looks like this:
GLKVector2 point{ { 0,0 } };
To me, this appears as point is being direct-initialized from a temporary created from the inner structure { 0,0 } and thus not offering any advantage over the first approach; the temporaries still have to be allocated and deallocated.
Or perhaps this issue is simply the nature of the union/struct layout used by GLKit types confusing the compiler.
Some clarification on this odd syntax, before further implementation in the code, would be much appreciated
The outer braces delimit the initializer for the object itself, the inner braces are the initializer for a member inside the object, e.g.
GLKVector2 v = { initializers-for-members };
where initializers-for-members is { 0, 0 } because the type has an array member, of two elements, and you initialize an array of two members with { a, b }.
C++ supports "brace elision" (8.5.1 [dcl.init.aggr] paragraph 11) which means nested braces can be left out of the initializer under certain circumstances, but in C++11 brace elision is only allowed for copy-initialization. This is why you don't need two sets of braces for the copy-init case, but do need them for direct-init.
Since C++11 was completed the rules have been changed by DR 1270 to allow brace elision in the direct-list-initialization case too, but that's a change made to the post-C++11 draft and not widely supported yet.
According to docs GLKVector2 is:
union _GLKVector2
{
struct { float x, y; };
struct { float s, t; };
float v[2];
};
typedef union _GLKVector2 GLKVector2;
That's why you need double braces, you're initializing by initializing a union member. Member-wise aggregate initialization occurs in-place. If it were a flat structure, your single-braces assumption would work. This also happens when initializing an std::array (e.g. std::array<int, 2> a{{1, 2}}) because it's a POD aggregate, and no temporary C arrays are involved.
You should look at aggregate initialization.
EDIT
Actually, looking up the docs, there're rules about brace elision (only allowed in copy-initialization context) that may be of interest.