Is passing an enum value to an int parameter non standard? - c++

Example:
enum SomeEnum
{
DD,
PP,
NN
};
void someFunc(int a)
{
}
int main()
{
SomeEnum e = DD;
someFunc(a) // calls someFunc with value 0
return 0;
}
This works in MSVC but is it non standard?
Thanks

An enum has an underlying integer type (the type used to store the value of the enum), and the enum value can be implicitly converted to that integer type's value.
In your case the underlying type is int, and the value is 0. Everything is okay.

The identiļ¬ers in an enumerator list are declared as constants that have type int and may appear wherever such are permitted.
6.7.2.2 here http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

It's absolutely valid because, as GManNickG and tomato said, all enums are of type int (in fact, in C, enums ARE ints and you can assign them values outside the scope of the enum.
typedef enum _foo
{
val1 = 57
} foo;
...
foo f = 99; // compiles in C but not C++
In fact, most compilers won't complain if the function argument type was bool or float or some other primitive number type because enums act as constant integer values like 0, -1, 200, etc...
What I'd say though, is that, if you have control over someFunc's signature and you want to ensure in C++ that no invalid values are passed in, change it to be
void someFunc(SomeEnum a);
for more typesafety
Also, always initialize your first enum value at least. I may be wrong on this but, back in the day, the compiler was allowed to pick an arbitrary starting value for your enum. Most of the time it picked 0 but not always. Even if that's not the case, it makes the code a little more self documenting and obvious.

Related

Templated function can't convert 'int' to nullptr_t

TL;DR:
Why can't templated functions access the same conversions that non-templated functions can?
struct A {
A(std::nullptr_t) {}
};
template <typename T>
A makeA(T&& arg) {
return A(std::forward<T>(arg));
}
void foo() {
A a1(nullptr); //This works, of course
A a2(0); //This works
A a3 = makeA(0); //This does not
}
Background
I'm trying to write some templated wrapper classes to use around existing types, with the goal of being drop-in replacements with minimal need to rewrite existing code that uses the now-wrapped values.
One particular case I can't get my head around is as follows: we have a class which can be constructed from std::nullptr_t (here called A), and as such, there's plenty of places in the code base where someone has assigned zero to an instance.
However, the wrapper cannot be assigned a zero, despite forwarding the constructors. I have made a very similar example that reproduces the issue without using an actual wrapper class - a simple templated function is sufficient to show the issue.
I would like to allow that syntax of being able to assign zero to continue to be allowed - it isn't my favourite, but minimising friction to moving to newer code is often a necessity just to get people on board with using them.
I also don't want to add a constructor that takes any int other than zero because that's very much absurd, was never allowed before, and it should continue to be caught at compile time.
If such a thing is not possible, it would satisfy me to find an explanation, because with as much as I know so far, it makes no sense to me.
This example has the same behaviour in VC++ (Intellisense seems to be OK with it though...), Clang, and GCC. Ideally a solution will also work in all 3 (4 with intellisense) compilers.
A more directly applicable example is follows:
struct A {
A(){}
A(std::nullptr_t) {}
};
template <typename T>
struct Wrapper {
A a;
Wrapper(const A& a):a (a) {}
template <typename T>
Wrapper(T&& t): a(std::forward<T>(t)){}
Wrapper(){}
};
void foo2() {
A a1;
a1 = 0; // This works
Wrapper<A> a2;
a2 = 0; //This does not
}
Why has the compiler decided to treat the zero as an int?
Because it is an integer.
The literal 0 is a literal. Literals get to do funny things. String literals can be converted into const char* or const char[N], where N is the length of the string + NUL terminator. The literal 0 gets to do funny things too; it can be used to initialize a pointer with a NULL pointer constant. And it can be used to initialize an object of type nullptr_t. And of course, it can be used to create an integer.
But once it gets passed as a parameter, it can't be a magical compiler construct anymore. It becomes an actual C++ object with a concrete type. And when it comes to template argument deduction, it gets the most obvious type: int.
Once it becomes an int, it stops being a literal 0 and behaves exactly like any other int. Not unless it is used in a constexpr context (like your int(0)), where the compiler can figure out that it is indeed a literal 0 and therefore can take on its magical properties. Function parameters are never constexpr, and thus they cannot partake in this.
See [conv.ptr]/1:
A null pointer constant is an integer literal with value zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type [...]
So the integer literal 0 can be converted to a null pointer. But if you attempt to convert some other integer value, that is not a literal, to a pointer type then the above quote does not apply. In fact there is no other implicit conversion from integer to pointer (since none such is listed in [conv.ptr]), so your code fails.
Note: Explicit conversion is covered by [expr.reinterpret.cast]/5.

Why is int x = int(5) legal if int is not a class?

From what I understand it is legal to instantiate an integer in c++ like this:
int x = int(5);
As a Java programmer I would assume that this line of code calls the constructor of the integer passing "5" as an argument. I read though that int is not a class and thus has no constructor.
So, what does exactly happen in that line of code and what is the fundamental difference between initialising the int that way and this way:
int x = 5;
Thanks in advance!
I read though that int is not a class and thus has no constructor.
Yes, technically built-in types have no constructor.
what does exactly happen in that line of code
The integer literal 5 is explicitly cast to an int (a no-op for the compiler mostly) and assigned to x.
what is the fundamental difference between initialising the int that way and int x = 5;
Essentially, no difference. All the below expressions are the same in most cases, unless you're a language lawyer (for instance, the last would prevent narrowing i.e. raise an error if the value cannot be represented by the type):
int x = 5; // copy initialization
int y = int(5); // cast and copy initialization
int z = (int)5; // cast and copy initialization
int w(5); // direct initialization
int r{5}; // direct initialization
Read more about initializations for finer details and differences.
C++ and java work differently when it comes to types. While java (to my understanding) uses basic built in types and reference types, C++ uses a different type system.
C++ built in types includes fundamental types (bool, character types like char, integer types, floating point types, and void) as well as some other types such as reference types like double& or std::vector<std::sting>&& and pointer types.
In addition to those, C++ supports user defined types (structs, classes, enum and enum class). the standard library provides many user defined types such as std::string.
it turns out the int a(5); notation is NOT reserved for user defined types only, The ,language supports value initialization that way. in C++11 it is also legal to say int a{5};
now about value assignment:
int a; //declaration
a=5; //assignment
int b(5); //declaration+initialization
int c=5; //declaration+initialization (NO assignment)
If a variable has not been declared, there is no assignment, the compiler parses int c=5; just like int c(5);

Detecting the types of individual enumeration constants

The code below prints unsigned int as the underlying type of all the constants inside the enum Test
#include <iostream>
#include <type_traits>
#include <typeinfo>
#include <cxxabi.h>
struct Test
{
enum { a = true, b = 1 };
};
static_assert(std::is_same<
std::underlying_type_t<decltype(Test::a)>,
std::underlying_type_t<decltype(Test::b)>>::value, ""
);
int main()
{
int status;
auto const& bi = typeid(std::underlying_type_t<decltype(Test::a)>);
std::cout << abi::__cxa_demangle(bi.name(), 0, 0, &status); // unsigned int
}
Live Example. This also happens if Test contains two separate enums with a and b as before.
Question: is it possible to detect that Test::a is initialized with bool and Test::b with int? Is there any experimental code for this in any of the Reflection Study Group proposals for C++17?
NOTE: I know I can work around it by replacing Test with
struct Test
{
static constexpr auto a = true;
static constexpr auto b = 1;
};
but I find the enum version slightly less verbose in its usage.
It is not possible and I consider it very unlikely that it will ever be possible.
The reason is that a and b are values of the (in your case unnnamed) enum. Thus they are, by definition, of the type of the enum, meaning: Of the same type. The expressions used to initialize them and their respective types are only used to "calculate" the underlying type of the enum, not of any individual values.
Considering this fact, I think that even reflection will not help to recover the information as the type of the initializing expression is abstracted away when you look at an enum's value.
What you'd need is an even deeper level of inspection where you'd need to access the expression used to initialize the values. That would mean that compilers will have to store a lot of additional information for every variable, values, etc. (since this would be a general thing not just for enums).
Consider what that means:
Increased compile times
Increased memory usage when compiling
Every value would effectively now have two types, the type it is declared as and the type of the expression it was initialized from
The last point is what I would consider the real catastrophe for the language. A value should have a single type, the expression used to initialized the value should not be accessible in any way. This is what abstraction is about, breaking it could lead to all kinds of subtle dependencies and a lot of complexity.
Disclaimer: This is just my personal opinion, I can't speak for anyone on the committee or in the working groups.
You could differentiate int enum values from non-ints as following:
struct Test
{
enum : short { a = true };
enum : int { b = 1 };
// enum : bool { c = false };
};
Unfortunately, VC2010 fails to compile a bool as underlying type, which is probably not considered as an integral type. This might not be an issue if your use case only requires separating ints from non-ints.

C++ enum used instead of const value

I just read some code similar to next one:
enum
{
width = 123,
height = 321,
position_x = 234
position_y = 432
};
...
Widget* w = CreateWidget(position_x, position_y, width, height);
Is there any reason to use enum in this case instead of macros or const values?
EDIT: Is it correct to use enum like this? Is this usage considered some kind of abuse in enum usage?
There are plenty of reasons not to use macros. The enum in the question is scoped and won't interfere with the same identifier used in different scopes, so you can for example, defined a member position_x in a class without the macro mangling your class definition.
Comparing the enum to a constant, there are people that prefer the enum as it is guaranteed that it will not add to the binary size of the executable. In the case of a constant, it may add (a bit, well, actually an int) to the size of the binary.
No, there's no special reason to choose an enum over macros or const int values in this case.
Editorial note: It's certainly legal code to use enum in this fashion, but it is a bit strange looking at first glance.
In this particular case, there doesn't appear to be any real deciding factor when choosing between an enum and constant values. Both are better than a macro however, and macros should be avoided in general.
More generally, there are some differentiating aspects between an enum and constants:
enums are distinct types, which is more expressive than integral values
you can take the address of a constant, but you can't take the address of an enum value
It may be more convenient to use enum than static const int as a class member.
class A
{
static const int Foo = 42;
// may have to define A::Foo somewhere
};
class B
{
enum { Foo = 42 };
// done
};

How does multiplication work for C++ enums?

We have a template conversion function intended for use with numeric datatypes. Inside it contains a construct that makes it not compile with types like pointers.
template<class To, class From>
To ConvertTo( From what )
{
assert( 2 * what == what * 2 ); // this will not compile for pointers
//skipped
}
This function compiles and works allright when a enum is passed as the second template parameter:
enum TSomeEnum {
SE_First,
SE_Second
};
TSomeEnum enumValue = SE_First;
int result = ConvertTo<int>( enumValue );
The above code compiles and runs as intended on VC++7.
How does the operation * work for enums? Is it a way to undefined behaviour?
Enums degrade to ints (old C feature) so that why this is working. I don't think it's undefined behaviour although it might be the sort of behaviour you don't want/expect.
Enums are promoted to integers for arithmetic operations.
The values of enums are always numbered up from zero (this is part of the standard), but you can specify whatever values you like. Automatic numbering then procedes incrementally from the last item you explicitly numbered.
The compiler uses the smallest integral type that contains all of the values of an enum to store it.
eg:
enum foo
{
VALUE0, /* = 0 */
VALUE1, /* = 1 */
VALUE3 = 1234,
VALUE4 /* = 1235 */
};
I think peterchen has the ideal answer, but if boost is too much for you, change your current version to:
template<class To, class From>
To ConvertTo( From what, From checkConvertableToInt = 2 )
{
}
C++ doesn't allow implicit conversions from integers to enumerations. Similarly, it's only possible to implicitly convert the null pointer constant (0) to a pointer type.
Some of the other answers refer to an enumeration being implemented as an int or being promoted to an int. This is not true. In C++, an enumeration is defined to have an underlying type that is capable of storing all values in the enumeration. The following enumeration will therefore have an underlying type of 'unsigned int' or a larger signed/unsigned integral type:
enum E {
E0
, E1=MAX_INT+1u
};
The enumeration then follows the rules for its underlying type when you use it in an operation.
Timo described why it works.
For a safer test, you could use boost::TypeTraits
Richard Corden's answer gives a good mechanism to force enums to fail in the assert, however the side effect can cause problems, as you cannot be sure the code inside the assert will always run.
This test should probably by placed in a generic function to avoid the side effects.
template<class To, class From>
To ConvertTo( From what )
{
assert( testIntegralType(what) );
}
template<class T>
void testIntegralType( T val )
{
T v1 = val;
assert( 2*v1 == 2*val );
}
enums are stored as integers, and can even be cast to integers.
The compiler probably stored your above enum as
enum TSomeEnum {
SE_First = 0,
SE_SEcond = 1
};
I'm not sure if this numbering system is part of the C/C++ standard or just how compilers do it, but I've always seen them in order.
Anyway, to answer your question, since they're stored as ints underneath, it should be able to be multiplied. Some compilers may give you a warning about type conversion though.