Specification that C++ enums are default constructible? - c++

C++ enumeration types appear to be "default constructible":
enum UE { a=1, b, c };
enum class SE { a=1, b, c };
int main() {
UE ue;
SE se;
}
How can this be explained from the standard?
I mean - let's say we wanted to change the standard to make it so they weren't default constructible. Which clauses would change?

It's all in [dcl.init]/7:
To default-initialize an object of type T means:
If T is a (possibly cv-qualified) class type, constructors are considered. The applicable constructors are enumerated
([over.match.ctor]), and the best one for the initializer () is chosen
through overload resolution. The constructor thus selected is called,
with an empty argument list, to initialize the object.
If T is an array type, each element is default-initialized.
Otherwise, no initialization is performed.
UE and SE match the third bullet, like fundamental types. So the initializaiton is simply a no-op, and they are left with an indeterminate value.
This is also the bullet list you'd need to tackle first to make enumerations not default initializable.

Don't let the class in enum class confuse you: It is still considered a non-class type. The syntax for scoped enums just happens to co-opt the class keyword, so as not to add yet another reserved word to the language.

There is a draft specification here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4778.pdf
See section 9.6, Enumeration declarations.
I think you may be coming from Java, in which enumerations are classes. In C++, enumerated values are just integer constants. The type of the constant is generally int although it can be explicitly defined.
Since the enumeration is just an int, when you declare one without an initializer, there's no default constructor involved; you just get an uninitialized int.

Related

What is instantiated when a POD structure is declared without using a default constructor?

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.

Does the default constructor represent zero for generic types in C++

Assume there is a function and it works
template <typename T>
Range<T> IRange(T lower, T upper){
....
}
So
IRange(0,5)
will produce a Range with values [0,1,2,3,4]. The exact behaviour of Range class is not important.
However now I want a helper function
template <typename T>
Range<T>
Repeat(T t){
return IRange(T(), t);
}
so that
Repeat(5)
will also produce a Range with values [0,1,2,3,4].
The question is:
Is using T() to get the zero value generically an ok thing to do?
I think so but something nags at me that this might be wrong.
Mostly this class will be used with integer types but anything else that fits the concept should work as well.
If T is int, then
int i = int();
value-initializes i, which for int means it is zero initialized so that it holds value 0. (Note that this syntax does not require the presence of a constructor for built-in types such as int.)
In general, T() is value initialization and the value depends on what T is as defined below:
The effects of value initialization are:
1) if T is a class type with at least one user-provided constructor of any kind, the default constructor is called; (until C++11)
1) if T is a class type with no default constructor or with a user-provided or deleted default constructor, the object is default-initialized; (since C++11)
2) if T is a non-union class type without any user-provided constructors, every non-static data member and base-class component of T is value-initialized; (until C++11)
2) if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor; (since C++11)
3) if T is an array type, each element of the array is value-initialized;
4) otherwise, the object is zero-initialized.
Does the default constructor represent zero for generic types in C++
No, it obviously doesn’t in general, e.g. for std::vector<char>().
However, you need to think in terms of constraints or, equivalently, concepts. For what conceptual T types do your functions IRange and Repeat make sense? From what you’ve written, it seems clear that the types need to be somehow conceptually numeric.
C++ doesn’t yet have formal definitions of such concepts (C++20 will gain some) but defining an ad-hoc concept for numeric types is relatively straightforward (thought here are important subtleties) by basing them on what int does.
For your purpose this leaves you exactly at P.W’s answer: For numeric types, T() is a good candidate for the identity under addition (aka. 0). Equivalently, you can use “uniform” initialisation1, T{}, and some people prefer this especially for generic code.
1 Unfortunately “uniform initialisation” isn’t, but you can ignore this in your case.

Enum class initialization with int

I found out some interesting thing when I programming:
enum class Foo {
FOO_THING,
FOO_TOO
};
int main() {
Foo foo{1}; // It is OK
Foo foo2(1); // It is an invalid
}
Could you tell me, why foo{1} is OK for compiler, and why foo2(1) is invalid ?
Compiler GCC (g++ (Ubuntu 7.3.0-21ubuntu1~16.04) 7.3.0) says:
$ g++ -Wall -std=c++17 foo.cpp
error: cannot convert ‘int’ to ‘Foo’ in initialization
Foo foo2(1);
I really want to know underlying mechanics. :)))
Edit: Maybe it is some compiler bug...
C++17-specific documentation has the following for braced initializer
Otherwise, if T is a enumeration type that is either scoped or
unscoped with fixed underlying type, and if the braced-init-list has
only one initializer, and if the conversion from the initializer to
the underlying type is non-narrowing, and if the initialization is
direct-list-initialization, then the enumeration is initialized with
the result of converting the initializer to its underlying type.
So foo seems to be conforming valid C++17, but foo2 being not braced initialized is not valid.
To understand the reasons why the two syntax are not both legit you must consider that scoped enums were introduced with standard c++11 to enforce static type checking and have scoped identifiers (i.e. no name pollution anymore).
Foo foo(1) is not working because implicit conversion from integer type to scoped enum is forbidden, otherwise you lose the benefit of scoped enums, and to avoid conflicts during overload resolution.
When using Foo foo{1} you are using list initialization that was introduced with c++11 too, but got an upgrade with c++17, that consist in implicit conversion from int value to enum as reported here, if a set of requirements are satisfied:
Both scoped enumeration types and unscoped enumeration types whose
underlying type is fixed can be initialized from an integer without a
cast, using list initialization, if all of the following is true:
the initialization is direct-list-initialization
the initializer list has only a single element
the enumeration is either scoped or unscoped with underlying type fixed
the conversion is non-narrowing.
This makes it possible to introduce new integer types (e.g. SafeInt) that enjoy the same existing calling conventions as their underlying integer
types, even on ABIs that penalize passing/returning structures by
value.
This syntax is safe and will not interfere with legacy code (written before c++11) because both scoped enums and list-initialization did not exist at the time. Furthermore, as reported in the quote, this enables the use of new integer types (like those of the SafeInt library) without the need to force a static cast for enum types in code that conforms to modern c++ syntax.

Default constructor of primitive types in assignment to anonymous instance

Consider the following code:
#include <iostream>
template<class T>
void f(T& t)
{
t = T();
}
int main()
{
int x = 42;
f(x);
std::cout << x;
}
Does the C++11 standard define what the output shall be? My compiler outputs 0, however I was under the impression the default constructor of a primitive type is a null operation or undefined behaviour.
There's no "default constructor" involved in your code. Only class types can have constructors. Scalar types have no constructors, default or otherwise.
The T() syntax creates a temporary object initialized by so called value-initialization. Value-initialization resolves to constructor call only for class types, and only for those with user-defined constructors (with some nuances in C++11). For other types value-initialization does not involve any constructors at all. It proceeds in accordance with its own specific and rather elaborate initialization rules that define the initial value of the data directly, without involving any constructors (see 8.5 in the language specification).
For scalar types value-initialization performs zero-initialization. This is why your code is guaranteed to output zero. The exact specifics of the abstract initialization process changed between the versions of C++ language standard, however since the beginning of times C++ language guaranteed that T() expression for T == int evaluated to zero. I.e. even in C++98 your code will output zero.
It is a common misconception that all these T(...) expressions somehow necessarily imply constructor calls. In reality, T(...) expression is a functional cast expression (regardless of the number of arguments) (see 5.2.3 in the language specification), which might resolve to constructor call in some narrow set of specific situations and has nothing to do with any constructors in other situations.
For example, this code
struct S { int x, y; };
S s = S();
is guaranteed to initialize s with zeros (both s.x and s.y) despite that fact that class S has a default constructor. I brought up this example specifically to illustrate the fact that even in situations when the default constructor exists, the T() expression can still completely ignore it and work by its own rules instead.
Here is what the standard says regarding your question:
In 8.5. paragraph 10:
An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.
In 8.5. paragraph 7:
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.
emphasis mine. So, since int isn't even a class type, it falls under the last rule, and gets zero-initialized, so it's an absolutely correct behavior.

Initializiation confusion

Not sure of the appropriate title, but it stems from this discussion:
Do the parentheses after the type name make a difference with new?
On Visual Studio 2008, when I run the following code:
struct Stan
{
float man;
};
int main()
{
Stan *s1 = new Stan;
Stan *s2 = new Stan();
}
Examining the locals, s1 has an uninitialized float with a random value. s2 is value initialized to 0.
However, if I add a string data member, the float in both instances is uninitialized.
struct Stan
{
std::string str;
float man;
};
However, the string in both instances is initialized. I tried adding other non-POD classes instead of a string, but the latter case only occurs if I add a string data member. I gather that adding a string still keeps it a POD class? If it wasn't a POD class, then it should have value initialized regardless of the parenthesis, right? Any ideas why floats(and other primitive data types for that matter) aren't initialized when I add a string data member?
Adding a string stops the struct from being a POD class because a POD class must be an aggregate class with no members of type non-POD-struct and std::string has (amongst other things) user-declared constructors which makes it a non-POD-struct.
This is a known bug/feature of Visual Studio 2008. It doesn't support C++03 value initialization for non-POD types such as the structure in your second example.
With the struct as in your second example what should happen is the float is not initialized by new Stan but is zero initialized in new Stan().
Types with a user declared default constructor are initialized by calling that constructor in all cases, this happens correctly.
See here and here.
The behavior that you observe is a conforming behavior from the point of view of C++98 specification of the language.
The type in your first example is a so-called POD (Plain Old Data) type. When you apply the () initializer to a POD class type it performs zero-initialization of all data members of the class.
The type in your second example is not a POD type (because of the non-POD member str). When you apply the () initializer to a non-POD class type, it simply calls the class constructor to perform initialization. In your case the implicit [compiler-provided] constructor will properly initialize the str member, but will not do anything for the man member, which is why you see garbage in it.
Again, this is the correct behavior in C++98. It was changed in C++03. In C++03 the () initializer will perform value-initialization. In accordance with the rules of value-initialization, in your second example, the man field should also get zero-initialized in response to () initializer.
See also https://stackoverflow.com/questions/1976185/what-is-the-difference-between-new-myclass-vs-new-myclass for more detailed explanation .