I believe that until C++14 a bit field of a struct declared as int
was still interpreted as either signed
or unsigned
, the interpretation being implementation defined. Reference: http://en.cppreference.com/w/cpp/language/bit_field.
Is this still the case in C++14? I.e., is the code below guaranteed to work as inteded?
#include <iostream>
struct X
{
int f:3;
};
int main()
{
X x;
x.f = -2; // is this going to be indeed signed? It seems so.
std::cout << x.f << std::endl; // displays -2
}
A bit field can be any integral type or enumeration type. End of C++ only. The maximum bit-field length is 64 bits. To increase portability, do not use bit fields greater than 32 bits in size.
In C and C++, native implementation-defined bit fields can be created using unsigned int , signed int , or (in C99) _Bool . In this case, the programmer can declare a structure for a bit field which labels and determines the width of several subfields.
In programming terminology, a bit field is a data structure that allows the programmer to allocate memory to structures and unions in bits in order to utilize computer memory in an efficient manner. Since structures and unions are user-defined data types in C, the user has an idea of how much memory will they occupy.
According to C++11 standard §9.6/p3 Bit-fields [class.bit] (Emphasis Mine):
A bit-field shall not be a static member. A bit-field shall have integral or enumeration type (3.9.1). It is implementation-defined whether a plain (neither explicitly signed nor unsigned)
char
,short
,int
,long
, orlong long
bit-field is signed or unsigned. Abool
value can successfully be stored in a bit-field of any nonzero size. The address-of operator & shall not be applied to a bit-field, so there are no pointers to bitfields. A non-const reference shall not be bound to a bit-field (8.5.3). [ Note: If the initializer for a reference of type const T& is an lvalue that refers to a bit-field, the reference is bound to a temporary initialized to hold the value of the bit-field; the reference is not bound to the bit-field directly. See 8.5.3. —end note ]
So you're correct for the first part. Indeed until C++14 a bit field of a struct declared as signed
was still interpreted as either signed
or unsigned
, the interpretation being implementation defined.
As already mentioned in this comments by @T.C. Defect reports referring to the issue were made DR739, DR675. Resulting in the following resolutions in C++14 standard:
The wording "It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char
, short
, int
, long
, or long long
bit-field is signed or unsigned.", was removed, and the C++14 wording now is:
A bit-field shall not be a static member. A bit-field shall have integral or enumeration type (3.9.1). A bool value can successfully be stored in a bit-field of any nonzero size. The address-of operator & shall not be applied to a bit-field, so there are no pointers to bit-fields. A non-const reference shall not be bound to a bit-field (8.5.3). [ Note: If the initializer for a reference of type const T& is an lvalue that refers to a bit-field, the reference is bound to a temporary initialized to hold the value of the bit-field; the reference is not bound to the bit-field directly. See 8.5.3. —end note ]
Also in §C.1.8 Clause 9: classes [diff.class] the following section was added:
9.6
Change: Bit-fields of type plain int are signed.
Rationale: Leaving the choice of signedness to implementations could lead to inconsistent definitions of template specializations. For consistency, the implementation freedom was eliminated for non-dependent types, too.
Effect on original feature: The choice is implementation-defined in C, but not so in C++.
Difficulty of converting: Syntactic transformation.
How widely used: Seldom.
Consequently, in C++14 bit-fields of type plain int
are signed and the code posted is guaranteed to work as intended.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With