TL;DR: I am looking for a C++14 equivalent of the following C++20 MWE:
template<int sz>
struct bits {
int v; // note explicit(expr) below
explicit(sz > 1) operator bool() const { return bool(v); }
};
int main() {
bool c = bits<1>{1}; // Should work
bool d = bits<3>{1}; // Should fail
}
Context:
We have a C++ class bits<sz>
representing bitvectors of length sz
. Conversion to bool
used to be implicit for all sz
, but this proved to be error-prone, so we changed operator bool()
to be explicit.
However, 1-bit
bitvectors are (in our context) almost-completely equivalent to Booleans, so it would be desirable for operator bool()
to be implicit when sz == 1
.
This can be achieved with explicit(sz > 1)
in C++20, but we are targeting C++14.
I tried to overload the operator for sz == 1
, but it seems that the explicit
qualifier applies to it as well: the following does not work.
template<int sz>
struct bits {
int v;
explicit operator bool() const { return bool(v); }
};
template<> bits<1>::operator bool() const {
return bool(v);
}
int main() {
bool c = bits<1>{1}; // Fails: "No viable conversion"
}
Hence the question: How can I specify in C++14 that operator bool()
should be explicit only for sz > 1
?
I'm including some background below for curious readers.
Background:
This problem came up in the context of an embedded domain-specific language in C++. One of the business requirements is that operator==
returns a bit<1>
, not a bool
. This is working smoothly with GNU's libstdc++, but we're running into trouble with that requirement on macOS because libstdc++ there implements operator==
on std::array
using the version of std::equal
that takes a predicate, and implements that predicate using a struct whose operator()
returns bool
with body a == b
(which in our case returns a bits<1>
, causing a conversion error).
To make it concrete for curious readers, the following program compiles fine on GNU, but not on macOS, because of the way operator==
on std::array
is implemented:
#include <array>
struct S { explicit operator bool() const { return true; } };
struct T {};
S operator==(T, T) { return S(); }
int main() {
std::array<T, 1> arr = { T() };
return arr == arr;
}
That's because deep down in the implementation of ==
on arrays GNU libstdc++ has a test if (!(*it1 == *it2))
, which invokes the explicit operator bool()
on S
without trouble, where as on macOS the library uses if (!__pred(*it1, *it2))
with __pred
roughly equivalent to bool __pred(S a, S b) { return a == b; }
, which doesn't typecheck.