9

The Standard Library class template std::bitset<N> has a constructor (C++11 and onwards, unsigned long argument before C++11)

constexpr bitset(unsigned long long) noexcept 

Contrary to many best-practice guidelines, this single-argument constructor is not marked as explicit. What is the rationale behind this?

TemplateRex
  • 65,583
  • 16
  • 147
  • 283
  • 1
    The C++ standard library has a fair share of questionable design decisions. – n. 'pronouns' m. Sep 14 '14 at 13:14
  • @n.m. Sure, so I am looking for an answer to determine if I should submit a defect report for this one ;-) – TemplateRex Sep 14 '14 at 13:16
  • As a _guess_, because the operators are not overloaded to accept unsigned integers, and this way code like `mybitset |= 0x4;` should compile. – Andrew Lazarus Sep 17 '14 at 00:36
  • It *might* make sense for `std::bitset<32> bs = 0x0FFFFFFF;` to compile. Anyway, I'm doubtful that they'd make it explicit at this point - breaks too much code. – T.C. Sep 17 '14 at 11:41

1 Answers1

5

Explicit construction

The main objection against an explicit constructor is that copy-initialization from unsigned integers no longer works

constexpr auto N = 64;
std::bitset<N> b(0xDEADC0DE);  // OK, direct initialization
std::bitset<N> b = 0xDEADC0DE; // ERROR, copy initialization cannot use explicit constructors

Since std::bitset<N> is meant as a generalization of unsigned int, the constructor was probably made implicit to facilitate adapting existing C-style bit-twiddling code based on raw unsigned int. Making the constructor explicit would have broken much existing code (and adding it now will equally break much existing code).

UPDATE: doing some Standard archeology, I found N0624 from January 1995 that proposed to add the then brand-new keyword explicit to all single-argument constructors in the pre-Standard Library draft. This was put to a vote at a meeting in March 1995 (Austin). As documented in N0661, the unsigned long constructor for bitset was not made explicit (unanimous vote, but without motivation).

Mixed-mode bit-twiddling

However, even though bitset is easily initialized from unsigned long, there is otherwise incomplete mixed-mode setwise operations (&, | or ^):

 constexpr auto N = 512;
 std::bitset<N> b = 0xDEADC0DE; // OK
 std::bitset<N> c = b & 0xFFFF; // ERROR, cannot deduce template arguments for rhs

This could be remedied by proposing overloaded operators to support mixed-mode bit-twiddling:

 // @ from { &, |, ^ }

 template<std::size_t N> 
 bitset<N> operator@(unsigned long long lhs, const bitset<N>& rhs)

 template<std::size_t N> 
 bitset<N> operator@(const bitset<N>& lhs, unsigned long long rhs)

Overloaded operators as member functions

The schizophrenic nature of std::bitset with respect to mixed-mode functionality is also present in the operator== and operator!=. These are member functions that have implicit conversion on their rhs arguments, but not on their lhs argument (the this pointer, which is subject to template argument deduction). This leads to the following:

#include <bitset>
#include <iostream>

int main()
{
    constexpr auto N = 64;
    constexpr std::bitset<N> b = 0xDEADC0DE; // OK, copy initialization

    std::cout << (b == 0xDEADC0DE);     // OK, implicit conversion on rhs
    std::cout << (0xDEADC0DE == b);     // ERROR, no implicit conversion on lhs
}

The origins of this behavior stem from the 1992 proposal N0128. The timing of that proposal, which largely locked in the functionality of the future std::bitset, was prior to function templates having non-type template parameters. The only feasible workaround at the time was to make all overloaded operators member functions instead of non-member functions. This was never changed later on when more advanced template technology became available (see also this Q&A for why this might break code).

Community
  • 1
  • 1
TemplateRex
  • 65,583
  • 16
  • 147
  • 283
  • 3
    `b & 0xFFFF` [doesn't work anyway](http://coliru.stacked-crooked.com/a/f0ef8a78e3ed0272). – T.C. Sep 17 '14 at 12:56