I've been using enum class FooEnabled : bool { no, yes };
as a way to create type-safe bool
s. It works well, except I'd like to add explicit conversion to bool
, Boolean operators like operator!
, etc. I can do that like this:
template <typename Tag>
class TypedBool {
bool value;
explicit constexpr TypedBool(bool b) noexcept : value(b) {}
public:
static inline TypedBool no{false};
static inline TypedBool yes{true};
explicit constexpr operator bool() const noexcept { return value; }
constexpr TypedBool operator!() const noexcept { return TypedBool{!value}; }
// ...
};
using FooEnabled = TypedBool<struct FooEnabledTag>;
That works great, however no
and yes
aren't constexpr
, so I can't do if constexpr (FooEnabled::yes) {
for example. If I make no
and yes
be instead static constexpr
, clang is upset because TypedBool
is not a literal type. That appears to be because TypedBool
is incomplete at that point.
The simplest example of this is struct S { static constexpr S s; };
which gives
error: constexpr variable cannot have non-literal type 'const S'
struct S { static constexpr S s; };
^
note: incomplete type 'const S' is not a literal type
note: definition of 'S' is not complete until the closing '}'
struct S { static constexpr S s; };
Is there any way around this? I could make no
and yes
be a different type that implicitly converts to TypedBool<Tag>
, but that seems weird, because then auto x = FooEnabled::yes;
would make x
not be a FooEnabled
, so
auto x = FooEnabled::yes;
[](FooEnabled& x) { x = !x; }(x);
would fail.
Is there any way to have a class contain static constexpr
members that are its own type? The solutions I'm starting to think of all seem too ugly to mention (and those also have constexpr
limitations).