I have recently discovered that you cant have at the same time in class initialization and initializer list.
The following code fails :
struct s
{
int i=0;
};
int main() {
s s1; //s1.i = 0
//s s2={42}; //fails
return 0;
}
If I remove the in class initialization, the initializer list works fine !
Can someone explains me why a such thing is no allowed ?
In fact this is allowed in C++14.
struct s
{
int i=0;
};
int main() {
s s1;
s s2 = {42}; // succeeds
}
It's likely that your compiler just isn't implementing the new rule in C++14. The latest version of clang, however, accepts this and does the correct thing in C++14 mode.
When in-class initialization was added to C++11 it was specified such that it prevented a class from being an aggregate. This was done because at the time the aggregate concept was closely related to PoD types which need to be trivially constructible. Having an in-class initialization means that a type is no longer trivially constructible. Since then, however, the two concepts have become more independent, and so for C++14 a short proposal reversing that decision was accepted.
This initialization:
s s1 = { 42 };
requires that s be an aggregate, or that it have a valid constructor taking e.g an int or an std::initializer_list.
When you add a member initialization at the point of declaration, you render your class s a non-aggregate, so you can no longer use aggregate initialization.
You could use the same initialization syntax for your non-aggregate by adding a constructor:
struct s
{
s(int i) : i(i) {}
int i=0;
};
I believe this restriction has been relaxed for C++14.
See What are aggregates... for more information.
Related
Given the example code:
struct S {
char data[5];
int a;
};
When running the "Run code analysis" in Microsoft Visual Studio, It warns to initialize all variables.
Now I know you can do this a number of ways, create a default constructor such as:
S() :
data{0},
a{0} {
}
That makes the warning go away. But what if you don't want to manually create the default constructor.
something like:
struct S {
char data[5];
int a = 0;
};
gets rid of the warning for a but not data, though you can fix that by adding {} after like so: char data[5]{}; this seems to make the code analysis happy.
That got me thinking, you can also initialize a like int a{0};
So my question is, are these all valid, and which is preferred?
Side note: I noticed std::array has _Ty _Elems[_Size]; which it never initializes anywhere, nor does it have {} after it, I'm assuming they just ignore this warning? Or are they doing something I'm not noticing to "fix" the warning?
Also wanted to add that this code:
#include
#include
template<class T, std::size_t N>
struct static_vector {
typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N] = {0};
T& operator[](std::size_t pos) {
return *std::launder(reinterpret_cast<T*>(&data[pos]));
}
};
int main(int argc, char**) {
static_vector<int, 10> s;
s[0] = argc;
return s[0];
}
under gcc9.1 -std=c++17 -Wall produces no warnings,
yet the same code under clang8.0 -std=c++17 -Wall gives me:
warning: suggest braces around initialization of subobject [-Wmissing-braces]
typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N] = {0};
^
{}
I see that I can set it to = {}; which fixes it, just wondering why one compiler would produce a warning when the other doesn't? Which one is to spec?
The guideline from CPPCoreGuidelines on this states: Don’t define a default constructor that only initializes data members; use in-class member initializers instead
So you can just do:
struct S {
char data[5] = {0};
int a = 0;
};
As to your other question about the lack of warning related to std::array, GCC has a note which states:
Warnings from system headers are normally suppressed, on the assumption that they usually do not indicate real problems and would only make the compiler output harder to read.
I believe this would be true of MSVC as well.
In C++ for each declarator, the initializer may be one of the following:
1. ( expression-list )
2. = expression
3. { initializer-list }
The description for these are as follows:
comma-separated list of arbitrary expressions and braced-init-lists in parentheses
the equals sign followed by an expression
braced-init-list: possibly empty, comma-separated list of expressions and other braced-init-lists
Well which type of initialization to prefer actually depends upon context. To initialize data members in a class I personally prefer in class initialization using braced initializer, as in that case we don't have to write a user defined default constructor, compiler generated one is always efficient.
Class members
Non-static data members can be initialized with member initializer
list or with a default member initializer.
In your case you can probably use:
struct S {
char data[5] = {0}; //initialize by zero
int a = 0;
};
or to give them different values also:
struct S {
char data[5] = {0,1,2,3,4};
int a = 0;
};
For more info see Initialization
Initializing a struct with default values is trivial:
struct X { int a; int b = 2; };
and initializing a struct with a brace initializer is trivial too:
X x = {1, 3};
Suprisingly the init code won't compile, until I remove the default value. So, how would I do the init in such a case? I'd like to keep X a POD without c-tor.
Here is some documentation relevant to the problem:
http://en.cppreference.com/w/cpp/language/aggregate_initialization
In c++11 your code is invalid. In c++14 it is valid again.
In C++11 adding a default initialization prevents braced init from being valid. In C++14, it does not.
A way to solve your problem in C++11 would be to write a constructor with the value for a and the b value with a default.
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.
I saw a code where a programmer used curly braces to initialize a variable
int var{ 5 };
instead of using the assignment operator
int var = 5;
I know assigning a value to lhs variable using curly braces is a C++11 syntax. Is there any difference between using the two?
Thank you for replies.
They are different kinds of initialization:
T a{b}; // list initialization
T a = b; // copy initialization
T a(b); // direct initialization
There is no difference for ints but there can definitely be differences for other types. For instance, copy initialization might fail if your constructor is explicit, whereas the other two would succeed. List initialization disallows narrowing conversions, but for the other two those are fine.
As far as I know, there is no difference in the two for integers. The {} syntax was made to(however, not limited to, because it is also used for initializer_list) prevent programmers from triggering http://en.wikipedia.org/wiki/Most_vexing_parse, and so instead of std::vector<int> v() to initialize v you write std::vector<int> v{};.
The {} has different behaviours depending on the usage, it can be a call to constructor, a initializer list and even a list of values to initialize members of user-defined class in order of definition.
Example of the last:
class Q{
public:
int a;
int b;
float f;
};
int main()
{
Q q{2, 5, 3.25f};
}
Could someone please help me understand why the following code does not compile (g++ 4.8). My understanding is that one could initialize a POD
#include <iostream>
#include <type_traits>
struct my_int
{
int val_;
};
struct B : public my_int
{
};
int main()
{
std::cout << std::is_pod<my_int>::value << std::endl;
std::cout << std::is_pod<B>::value << std::endl;
const my_int v = { 123 };
//const B v2 = { 123 }; // does not compile with g++ 4.8.
return 0;
}
Compilation is:
g++ -std=c++11 t.cxx
t.cxx: In function 'int main()':
t.cxx:24:21: error: could not convert '{123}' from '<brace-enclosed initializer list>' to 'const B'
const B v = { 123 };
^
EDIT:
Thanks to everyone answer I now understand the concept of aggregate initialisation. I missed the fact that aggregate could not have base class. Therefore my current implementation plans needs to be changed. I wanted to do something like:
template < typename T >
struct base_class
{
int val_;
};
struct MyInt : public base_class<int>
{
void Func1() {}
};
struct MyDouble : public base_class<double>
{
void Func2() {}
};
I'll rework the above code to avoid the use of subclass to introduce special member functions, while avoid code duplication.
Disclaimer
Take the following with a grain of salt as it is my interpretation of things. I am by no means an expert. (Also I have some doubts about the aggregate - initializer list relation which I stated here.)
Answer
As far as I can tell this is not possible because the aggregate initialization of v2 would be applied to the non aggregate class type B.
From this answer you can take that aggregates must not have a base class,
which makes B a non aggregate and therefore not initializable by a brace enclosed initializer list.
On the other hand std::is_pod might not do what you think it does because the POD definition has changed in C++11. Therefore, it does not give you a hint if the type that is a POD can be initialized with such an aggregate initializer.
Addition
I am mainly discussing aggregate initialization here, but the more general term for this is list initialization which is less restrictive. However, checking all the cases I found in the linked resource there is no possibility to do list initialization either
because (following the resource's list of effects of an initializer list):
the initializer list is not empty
B is not an aggregate
B is not a specialization of std::initializer_list
B has no
constructor taking an initializer list
constructor fitting the signature of the list
B is no reference type
B cannot be copy-initialized from 123 and not direct-initialized because there is no constructor taking an int
B is not value-initialized because the initializer list ist not empty