Document number:N3110=10-0100
Author:Jonathan Wakely
Date:2010-08-04

Revision history

This is an update of the paper attached to the GB ballot comments on the FCD. Changes requested by the LWG are adding static to constexpr data members, changing the value for bitmask constants to see below, and not adding constexpr to the compound assignment operators for the example bitmask type. This version also fixes the table references for the regex_constants which incorrectly used the table numbers from an earlier draft, not the FCD.

Problems with bitmask types in the library

The library defines the bitmask types (17.5.2.1.3) ios_base::fmtflags, ios_base::iostate, ios_base::openmode, regex_constants::syntax_option_type and regex_constants::match_flag_type. Each is defined as an unscoped enumeration without a fixed underlying type. Each has operator&, operator| and operator~ overloaded.

The library also defines the enumerated types (17.5.2.1.2) ios_base::seekdir and regex_constants::error_type.

There are several problems related to these types:

Bitmask operators declared as members of ios_base

The bitwise operators for the ios_base bitmask types are declared in the body of ios_base, making them members of the class, but they should be non-member functions. This is largely an editorial issue, but is addressed by the proposed changes below.

Bitmask type operators defined for enumerated types

As specified in 27.5.2p1 and 27.5.2.1.5p1, ios_base::seekdir is an enumerated type (17.5.2.1.2) not a bitmask type (17.5.2.1.3) and so should not have bitwise operators defined. The operators were added by the constexpr proposal, n2349, but should be removed.

The same applies to regex_constants::error_type (28.5.3) although 28.5.3p1 incorrectly uses the term "enumeration type" instead of "enumerated type".

operator^ is missing

n2349 proposed the following additional overload which is missing from the FCD:

constexpr fmtflags operator^(fmtflags lhs, fmtflags rhs)
{
   return fmtflags(int(lhs) ^ int(rhs));
}

This operator would be needed for all the bitmask types if they were defined as enumeration types.

operator~ is defined incorrectly

The FCD specifies:

constexpr fmtflags operator~(fmtflags f)
{
   return fmtflags( f);
}

This seems to be an editorial error as n2349 proposed:

constexpr fmtflags operator~(fmtflags f)
{
   return fmtflags(~(f));
}

This isn't right either, since it will lead to infinite recursion. I think the intended definition is:

constexpr fmtflags operator~(fmtflags f)
{
   return fmtflags(~int(f));
}

The underlying type should be fixed

Bitmask types need operator~ in order to clear a value from the bitmask type, as show in 17.5.2.1.3 paragraph 4:

— To clear a value Y in an object X is to evaluate the expression X &= ~Y.

However the definition for fmtflags above does not have well-specified behaviour if the underlying type is smaller than int, because ~int(f) is likely to produce a value outside the range of fmtflags.

The same problem is present in the example implementation in 17.5.2.1.3 because int_type is only stated to be capable of holding all the values of bitmask so there is no guarantee that sizeof(int_type) == sizeof(bitmask) and therefore no guarantee that the code given for operator~ works as intended.

This could be solved by giving the enumeration a fixed underlying type which is the same type as used for the conversions used by the bitwise operators. An alternative solution would be to do all conversions to and from std::underlying_type<bitmask>.

Value of goodbit is not unspecified.

The synopsis in 27.5.2 includes:

goodbit = unspecified,

but 27.5.2.1.3 specifies that goodbit has the value zero.

Bitmasks types are inconsistent with the requirements and over-specified.

In C++03 implementions were allowed to use any of the options in 17.5.2.1.3 for the bitmask types defined by the library and e.g. std::ios::in is an object of bitmask type with an implementation-defined value. The FCD specifies that bitmask types are implemented as enumerations and std::ios::in is an enumerator not an object. Not only is this inconsistent with the stated requirements in 17.5.2.1.3 but it means that existing implementations which define bitmask types as an integer type must change, and it breaks the following valid C++03 code:

#include <ios>
std::ios::openmode* p = &std::ios::in;

If all the library's bitmask types are required to be enumeration types then there is no reason for 17.5.2.1.3 to allow integer types or a bitset.

Existing C++03 implementations choose to implement bitmask types as integer types for efficiency, because enumerations with overloaded operators are less efficient without constexpr. Since the FCD defines the bitmask types as enumerations with overloaded constexpr operators, implementations cannot provide a common definition for C++03 and C++0x. This is of no benefit to users or implementations.

Proposed Solution

The over-specification of the library's bitmask types should be reverted. The example bitmask in clause 17 can be updated to use constexpr and an enumeration type with a fixed underlying type. constexpr can also be used for declaring the objects of bitmask type. Proposed wording for this change is given below.

An alternative solution would be to fix all the issues listed above except the over-specification, but this would still require existing implementations to change, has the chance of breaking valid C++03 programs, and leaves the definitions of bitmask types inconsistent with the requirements in Clause 17.

Proposed Wording

Modifications to Bitmask types

Make the following changes to 17.5.2.1.3:

The bitmask type bitmask can be written:
// For exposition only.
// int_type is an integral type capable of
// representing all values of bitmask 
enum bitmask : int_type {
  V0 = 1 << 0, V1 = 1 << 1, V2 = 1 << 2, V3 = 1 << 3, .....
};

static constconstexpr bitmask C0 (V0);
static constconstexpr bitmask C1 (V1);
static constconstexpr bitmask C2 (V2);
static constconstexpr bitmask C3 (V3);
  .....

// For exposition only.
// int_type is an integral type capable of
// representing all values of bitmask
constexpr bitmask operator& (bitmask X , bitmask Y ) {
  return static_cast<bitmask >(
    static_cast<int_type>(X ) &
    static_cast<int_type>(Y ));
}
constexpr bitmask operator| (bitmask X , bitmask Y ) {
  return static_cast<bitmask >(
    static_cast<int_type>(X ) |
    static_cast<int_type>(Y ));
}
constexpr bitmask operator^ (bitmask X , bitmask Y ){
  return static_cast<bitmask >(
    static_cast<int_type>(X ) ^
    static_cast<int_type>(Y ));
}
constexpr bitmask operator~ (bitmask X ){
  return static_cast<bitmask >(~static_cast<int_type>(X ));
}

Modifications to Class ios_base

Make the following changes to the synopsis in 27.5.2:

class ios_base {
public:
  class failure;


  typedef T1 fmtflags;
  static constexpr fmtflags boolalpha = unspecified;
  static constexpr fmtflags dec = unspecified;
  static constexpr fmtflags fixed = unspecified;
  static constexpr fmtflags hex = unspecified;
  static constexpr fmtflags internal = unspecified;
  static constexpr fmtflags left = unspecified;
  static constexpr fmtflags oct = unspecified;
  static constexpr fmtflags right = unspecified;
  static constexpr fmtflags scientific = unspecified;
  static constexpr fmtflags showbase = unspecified;
  static constexpr fmtflags showpoint = unspecified;
  static constexpr fmtflags showpos = unspecified;
  static constexpr fmtflags skipws = unspecified;
  static constexpr fmtflags unitbuf = unspecified;
  static constexpr fmtflags uppercase = unspecified;
  static constexpr fmtflags adjustfield = see below;
  static constexpr fmtflags basefield = see below;
  static constexpr fmtflags floatfield = see below;

  // 27.5.2.1.2 fmtflags
  enum fmtflags {
    boolalpha = unspecified,
    dec = unspecified,
    fixed = unspecified,
    hex = unspecified,
    internal = unspecified,
    left = unspecified,
    oct = unspecified,
    right = unspecified,
    scientific = unspecified,
    showbase = unspecified,
    showpoint = unspecified,
    showpos = unspecified,
    skipws = unspecified,
    unitbuf = unspecified,
    uppercase = unspecified,
    adjustfield = unspecified,
    basefield = unspecified,
    floatfield = unspecified,
  };

  constexpr fmtflags operator~(fmtflags f);
  constexpr fmtflags operator&(fmtflags lhs, fmtflags rhs);
  constexpr fmtflags operator|(fmtflags lhs, fmtflags rhs);



  typedef T2 iostate;
  static constexpr iostate badbit = unspecified;
  static constexpr iostate eofbit = unspecified;
  static constexpr iostate failbit = unspecified;
  static constexpr iostate goodbit = see below;

  // 27.5.2.1.3 iostate
  enum iostate {
    badbit = unspecified,
    eofbit = unspecified,
    failbit = unspecified,
    goodbit = unspecified,
  };

  constexpr iostate operator~(iostate f);
  constexpr iostate operator&(iostate lhs, iostate rhs);
  constexpr iostate operator|(iostate lhs, iostate rhs);


  typedef T3 openmode;
  static constexpr openmode app = unspecified;
  static constexpr openmode ate = unspecified;
  static constexpr openmode binary = unspecified;
  static constexpr openmode in = unspecified;
  static constexpr openmode out = unspecified;
  static constexpr openmode trunc = unspecified;

  // 27.5.2.1.4 openmode
  enum openmode {
    app = unspecified,
    ate = unspecified,
    binary = unspecified,
    in = unspecified,
    out = unspecified,
    trunc = unspecified,
  };

  constexpr openmode operator~(openmode f);
  constexpr openmode operator&(openmode lhs, openmode rhs);
  constexpr openmode operator|(openmode lhs, openmode rhs);



  typedef T4 seekdir;
  static constexpr seekdir beg = unspecified;
  static constexpr seekdir cur = unspecified;
  static constexpr seekdir end = unspecified;

  
  // 27.5.2.1.5 seekdir
  enum seekdir {
    beg = unspecified,
    cur = unspecified,
    end = unspecified,
  };

  constexpr seekdir operator~(seekdir f);
  constexpr seekdir operator&(seekdir lhs, seekdir rhs);
  constexpr seekdir operator|(seekdir lhs, seekdir rhs);

Revert the changes from n2349, making the following changes to 27.5.2.1.2:

  typedef T1 fmtflags;
  enum fmtflags;

1 The type fmtflags is a bitmask type (17.5.2.1.3). Setting its elements has the effects indicated in Table 112.

2 Type fmtflags also defines the constants indicated in Table 113.

  constexpr fmtflags ios_base::operator~(fmtflags f);

3 Returns: fmtflags( f).

  constexpr fmtflags ios_base::operator&(fmtflags lhs, fmtflags rhs);

4 Returns: fmtflags(int(lhs) & int(rhs)).

  constexpr fmtflags ios_base::operator|(fmtflags lhs, fmtflags rhs);

5 Returns: fmtflags(int(lhs) | int(rhs)).

Make the following changes to 27.5.2.1.3:

  typedef T2 iostate;
  enum iostate;

1 The type iostate is a bitmask type (17.5.2.1.3) that contains the elements indicated in Table 114.

  constexpr iostate ios_base::operator~(iostate f);

2 Returns: iostate(f).

  constexpr iostate ios_base::operator&(iostate lhs, iostate rhs);

3 Returns: iostate(int(lhs) & int(rhs)).

  constexpr iostate ios_base::operator|(iostate lhs, iostate rhs);

4 Returns: iostate(int(lhs) | int(rhs)).

Make the following changes to 27.5.2.1.4:

  typedef T3 openmode;
  enum openmode;

1 The type openmode is a bitmask type (17.5.2.1.3). It contains the elements indicated in Table 115.

  constexpr openmode ios_base::operator~(openmode f);

2 Returns: openmode(f).

  constexpr openmode ios_base::operator&(openmode lhs, openmode rhs);

3 Returns: openmode(int(lhs) & int(rhs)).

  constexpr openmode ios_base::operator|(openmode lhs, openmode rhs);

4 Returns: openmode(int(lhs) | int(rhs)).

Make the following changes to 27.5.2.1.5:

  typedef T4 seekdir;
  enum seekdir;

1 The type seekdir is an enumerated type (17.5.2.1.2) that contains the elements indicated in Table 116.

  constexpr seekdir ios_base::operator~(seekdir f);

2 Returns: seekdir(f).

  constexpr seekdir ios_base::operator&(seekdir lhs, seekdir rhs);

3 Returns: seekdir(int(lhs) & int(rhs)).

  constexpr seekdir ios_base::operator|(seekdir lhs, seekdir rhs);

4 Returns: seekdir(int(lhs) | int(rhs)).

Modifications to Bitmask Type regex_constants::syntax_option_type

Make the following changes to 28.5.1:

namespace std {
 namespace regex_constants {
  typedef bitmask_type syntax_option_type;
  constexpr syntax_option_type icase = unspecified;
  constexpr syntax_option_type nosubs = unspecified;
  constexpr syntax_option_type optimize = unspecified;
  constexpr syntax_option_type collate = unspecified;
  constexpr syntax_option_type ECMAScript = unspecified;
  constexpr syntax_option_type basic = unspecified;
  constexpr syntax_option_type extended = unspecified;
  constexpr syntax_option_type awk = unspecified;
  constexpr syntax_option_type grep = unspecified;
  constexpr syntax_option_type egrep = unspecified;

  enum syntax_option_type {
    icase = implementation-defined,
    nosubs = implementation-defined,
    optimize = implementation-defined,
    collate = implementation-defined,
    ECMAScript = implementation-defined,
    basic = implementation-defined,
    extended = implementation-defined,
    awk = implementation-defined,
    grep = implementation-defined,
    egrep = implementation-defined,
  };
  constexpr syntax_option_type operator~(syntax_option_type f);
  constexpr syntax_option_type operator&(syntax_option_type lhs, syntax_option_type rhs);
  constexpr syntax_option_type operator|(syntax_option_type lhs, syntax_option_type rhs);
 }
}

1 The type syntax_option_type is an implementation-defined bitmask type (17.5.2.1.3). Setting its elements has the effects listed in table 135. A valid value of type syntax_option_type shall have exactly one of the elements ECMAScript, basic, extended, awk, grep, egrep, set.

  constexpr syntax_option_type operator~(syntax_option_type f);

2 Returns: syntax_option_type(f).

  constexpr syntax_option_type operator&(syntax_option_type lhs, syntax_option_type rhs);

3 Returns: syntax_option_type(int(lhs) & int(rhs)).

  constexpr syntax_option_type operator|(syntax_option_type lhs, syntax_option_type rhs);

4 Returns: syntax_option_type(int(lhs) | int(rhs)).

Modifications to Bitmask Type regex_constants::syntax_option_type

Make the following changes to 28.5.2:

namespace std {
 namespace regex_constants{
  typedef bitmask_type match_flag_type;
  constexpr match_flag_type match_default{ 0 };
  constexpr match_flag_type match_not_bol = unspecified;
  constexpr match_flag_type match_not_eol = unspecified;
  constexpr match_flag_type match_not_bow = unspecified;
  constexpr match_flag_type match_not_eow = unspecified;
  constexpr match_flag_type match_any = unspecified;
  constexpr match_flag_type match_not_null = unspecified;
  constexpr match_flag_type match_continuous = unspecified;
  constexpr match_flag_type match_prev_avail = unspecified;
  constexpr match_flag_type format_default{ 0 };
  constexpr match_flag_type format_sed = unspecified;
  constexpr match_flag_type format_no_copy = unspecified;
  constexpr match_flag_type format_first_only = unspecified;

  enum match_flag_type {
    match_default = 0,
    match_not_bol = implementation-defined,
    match_not_eol = implementation-defined,
    match_not_bow = implementation-defined,
    match_not_eow = implementation-defined,
    match_any = implementation-defined,
    match_not_null = implementation-defined,
    match_continuous = implementation-defined,
    match_prev_avail = implementation-defined,
    format_default = 0,
    format_sed = implementation-defined,
    format_no_copy = implementation-defined,
    format_first_only = implementation-defined,
  };
  constexpr match_flag_type operator~(match_flag_type f);
  constexpr match_flag_type operator&(match_flag_type lhs, match_flag_type rhs);
  constexpr match_flag_type operator|(match_flag_type lhs, match_flag_type rhs);
 }
}

1 The type regex_constants::match_flag_type is an implementation-defined bitmask type (17.5.2.1.3). Matching a regular expression against a sequence of characters [first,last) proceeds according to the rules of the grammar specified for the regular expression object, modified according to the effects listed in table 136 for any bitmask elements set.

  constexpr match_flag_type operator~(match_flag_type f);

2 Returns: match_flag_type(f).

  constexpr match_flag_type operator&(match_flag_type lhs, match_flag_type rhs);

3 Returns: match_flag_type(int(lhs) & int(rhs)).

  constexpr match_flag_type operator|(match_flag_type lhs, match_flag_type rhs);

4 Returns: match_flag_type(int(lhs) | int(rhs)).

Modifications to Bitmask Type regex_constants::error_type

Make the following changes to 28.5.3:

namespace std {
 namespace regex_constants {
  typedef implementation-defined error_type;
  constexpr error_type error_collate = unspecified;
  constexpr error_type error_ctype = unspecified;
  constexpr error_type error_escape = unspecified;
  constexpr error_type error_backref = unspecified;
  constexpr error_type error_brack = unspecified;
  constexpr error_type error_paren = unspecified;
  constexpr error_type error_brace = unspecified;
  constexpr error_type error_badbrace = unspecified;
  constexpr error_type error_range = unspecified;
  constexpr error_type error_space = unspecified;
  constexpr error_type error_badrepeat = unspecified;
  constexpr error_type error_complexity = unspecified;
  constexpr error_type error_stack = unspecified;

  enum error_type {
    error_collate = implementation-defined,
    error_ctype = implementation-defined,
    error_escape = implementation-defined,
    error_backref = implementation-defined,
    error_brack = implementation-defined,
    error_paren = implementation-defined,
    error_brace = implementation-defined,
    error_badbrace = implementation-defined,
    error_range = implementation-defined,
    error_space = implementation-defined,
    error_badrepeat = implementation-defined,
    error_complexity = implementation-defined,
    error_stack = implementation-defined,
  };
  constexpr error_type operator~(error_type f);
  constexpr error_type operator&(error_type lhs, error_type rhs);
  constexpr error_type operator|(error_type lhs, error_type rhs);
 }
}

1 The type error_type is an implementation-defined enumerationed type (17.5.2.1.2). Values of type error_type represent the error conditions described in table 137:

  constexpr error_type operator~(error_type f);

2 Returns: error_type(f).

  constexpr error_type operator&(error_type lhs, error_type rhs);

3 Returns: error_type(int(lhs) & int(rhs)).

  constexpr error_type operator|(error_type lhs, error_type rhs);

4 Returns: error_type(int(lhs) | int(rhs)).