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
}
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 not specifically 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.