Should std::bitset have a constructor that takes a std::initializer_list?

488 views
Skip to first unread message

drt...@gmail.com

unread,
Mar 14, 2014, 8:27:26 PM3/14/14
to std-dis...@isocpp.org
It would be useful if std::bitset could be used like so:

// Compile time generated 100 bit bitset, with bits 1, 52 and 80 set to true.
static const auto mask = std::bitset< 100 >{ 1, 52, 80 };

Any reasons why this wouldn't work and/or shouldn't be included in the standard?

Anass Lasram

unread,
Mar 14, 2014, 10:04:47 PM3/14/14
to std-dis...@isocpp.org
I think the class offers enough functionality for the user to easily implement the function. Plus this form will easily conflict with bitset(unsigned long).


--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.

David Krauss

unread,
Mar 15, 2014, 3:47:02 AM3/15/14
to std-dis...@isocpp.org
See the StackOverflow Q&A.

An initializer_list constructor would indeed interact with the conversion from unsigned long long, but it should only be about the same as the container constructors that accept std::size_t (although those constructors are explicit).

The motivation is not only pretty syntax. std::bitset now supports constexpr semantics, but there is no way to set a bit except by specifying it in an unsigned long long value.

To make its compile-time capabilities more self-consistent, there should be an additional constructor or factory function. Also, it couldn’t hurt to make constexpr the non-member operator overloads.

Note that DrTwox also wants compile-time evaluation. I think this is a common scenario and bitset should be on par with enumeration bitset types.

A quick check in Clang C++1y mode shows that it supports initializer_list parameters in constexpr evaluations. Is this conforming?

Richard Smith

unread,
Mar 15, 2014, 8:43:35 PM3/15/14
to std-dis...@isocpp.org
On Sat, Mar 15, 2014 at 12:47 AM, David Krauss <pot...@gmail.com> wrote:
See the StackOverflow Q&A.

An initializer_list constructor would indeed interact with the conversion from unsigned long long, but it should only be about the same as the container constructors that accept std::size_t (although those constructors are explicit).

The motivation is not only pretty syntax. std::bitset now supports constexpr semantics, but there is no way to set a bit except by specifying it in an unsigned long long value.

Only the default constructor and the unsigned long long constructor are constexpr today. Perhaps it's time to make all the rest of bitset's member functions (and bitset<N>::reference's member functions) constexpr -- then it'd be easy to constant-initialize a bitset however you want. For instance:

template<unsigned N> constexpr bitset<N> make_bitset(std::initializer_list<size_t> values) {
  bitset<N> bits;
  for (size_t bit : values) bits.set(bit);
  return bits;
}
constexpr bitset<32> bits = make_bitset<32>({1, 3, 7, 11});

... should then work.
 
To make its compile-time capabilities more self-consistent, there should be an additional constructor or factory function. Also, it couldn’t hurt to make constexpr the non-member operator overloads.

Note that DrTwox also wants compile-time evaluation. I think this is a common scenario and bitset should be on par with enumeration bitset types.

A quick check in Clang C++1y mode shows that it supports initializer_list parameters in constexpr evaluations. Is this conforming?

Yes, as of N3471, initializer_list is a literal type, removing the only barrier to using it in constant expression evaluations.

David Krauss

unread,
Mar 15, 2014, 8:52:42 PM3/15/14
to std-dis...@isocpp.org
On 2014–03–16, at 8:43 AM, Richard Smith <ric...@metafoo.co.uk> wrote:

constexpr bitset<32> bits = make_bitset<32>({1, 3, 7, 11});

Ah, I didn’t expect that bitset::set could be constexpr despite being non-const, but I guess that’s the new rule.

bitset<100>().set( 77 ) would also be a constant expression then, right? (set returns *this.)

Richard Smith

unread,
Mar 15, 2014, 9:08:41 PM3/15/14
to std-dis...@isocpp.org
Yes. 

rhalb...@gmail.com

unread,
Mar 18, 2014, 4:45:51 AM3/18/14
to std-dis...@isocpp.org
On Sunday, March 16, 2014 1:43:35 AM UTC+1, Richard Smith wrote:
On Sat, Mar 15, 2014 at 12:47 AM, David Krauss <pot...@gmail.com> wrote:
See the StackOverflow Q&A.

An initializer_list constructor would indeed interact with the conversion from unsigned long long, but it should only be about the same as the container constructors that accept std::size_t (although those constructors are explicit).

The motivation is not only pretty syntax. std::bitset now supports constexpr semantics, but there is no way to set a bit except by specifying it in an unsigned long long value.

Only the default constructor and the unsigned long long constructor are constexpr today. Perhaps it's time to make all the rest of bitset's member functions (and bitset<N>::reference's member functions) constexpr -- then it'd be easy to constant-initialize a bitset however you want. For instance:

template<unsigned N> constexpr bitset<N> make_bitset(std::initializer_list<size_t> values) {
  bitset<N> bits;
  for (size_t bit : values) bits.set(bit);
  return bits;
}
constexpr bitset<32> bits = make_bitset<32>({1, 3, 7, 11});

... should then work.

It would be greatly appreciated if a greatly revamped std::bitset became part of the next Standard. 

In my opinion, std::bitset<N> is not some "dumb" bit container, but should be treated as a packed representation of a std::set<int> with max_size() == N, and boost::dynamic_bitset<Alloc> can be viewed as a packed representation of a std::set<int, Alloc>. The various bitwise and/or/xor operators of std::bitset can be viewed as optimized versions of std::set_intersection/std::set_union/std::set_symmetric_difference, and the bitwise shift operators are optimized versions of std::transform with a lambda that adds / subtracts an integer to all set members. The bit-level getters/setters are then equivalent to the insert/erase interface of std::set<int>.

Taking this level of abstraction even further, libstdc++ (and boost::dynamic_bitset) define Find_first_ and Find_next_ member function, which can be adapted to serve as bidirectional proxy-const_iterators (where operator* yields a proxy-const_reference with an implicit conversion to int). These proxy iterators/references are not true iterators and can therefore fail as usual in expressions that require multiple user-defined conversions. But they do not suffer from most of the other problems of std::vector<bool>::iterator because they are const. (they also differ from std::vector<bool>::iterators in that std::bitset<N>::iterator only iterates over 1-bits, skipping the 0-bits).

With that extra level of abstraction, a std::bitset would satisfy the same invariant as std::set<int>. E.g. std::is_sorted(begin(b), end(b)) would be true for every std::bitset b. Conversion to / from any other container or iostream also becomes very easy: std::copy(begin(vec), end(vec), std::inserter(bs, begin(bs)) just works. They also automatically work with Boost.Range. It gives the same convenience and performance for std::bitset as Howard Hinnant did for std::vector<bool> in libc++ (http://isocpp.org/blog/2012/11/on-vectorbool).

I have successfully implemented such a bitset with all constructors and bit-level getters/setters as constexpr, as well as bidirectional iterators and the full std::set<int> interface, along with non-member algorithm functions that encapsulate the various bit-twiddling routines  (http://tinyurl.com/oshuckp). My application is to use this in a boardgame engine to efficiently manipulate the board state (most of the top-level computer chess programs do similar things with raw 64-bit integer bit-twiddling and could use the same high-level abstraction).

If there is sufficient interest in this stuff, I could write a proper documentation and try to submit this to Boost or as a draft proposal.

David Krauss

unread,
Mar 18, 2014, 10:23:42 AM3/18/14
to std-dis...@isocpp.org

On 2014–03–18, at 4:45 PM, rhalb...@gmail.com wrote:

I have successfully implemented such a bitset with all constructors and bit-level getters/setters as constexpr, as well as bidirectional iterators and the full std::set<int> interface, along with non-member algorithm functions that encapsulate the various bit-twiddling routines  (http://tinyurl.com/oshuckp). My application is to use this in a boardgame engine to efficiently manipulate the board state (most of the top-level computer chess programs do similar things with raw 64-bit integer bit-twiddling and could use the same high-level abstraction).

If there is sufficient interest in this stuff, I could write a proper documentation and try to submit this to Boost or as a draft proposal.

Sounds pretty cool!

It might be better to put this functionality into a new container class rather than bloat the std::bitset interface.

I’m partial to allocators as a solution, so the class could be a partial specialization

template< size_t n >
class map< size_t, bool, bitset< n > >

bitset is of course not an Allocator. Depending on your viewpoint, that’s either hideous or ingenious. (IMHO this is also the way out of the std::vector<bool> quandary, give or take some modicum of respect for the Allocator interface.)

Barring standardization of such a hack, your idea probably needs a unique name to nestle alongside std:: brethren.

Reply all
Reply to author
Forward
0 new messages