Reputation: 1016
I have a Flags
class that behaves similarly to std::bitset
that is replacing bitpacked integers in an older codebase. To enforce compliance with the newer class, I want to disallow implicit conversion from int
types.
enum class Flag : unsigned int {
none = 0,
A = 1,
B = 2,
C = 4,
//...
};
class Flags {
public:
Flags();
Flags(const Flag& f);
explicit Flags(unsigned int); // don't allow implicit
Flags(const Flags&);
private:
unsigned int value;
};
I would like to allow implicit construction and assignment only from the Flag
and Flags
types. However, I would still like some function calls that take a Flags
parameter to accept a literal 0
, but not other integers:
void foo(const Flags& f);
foo(Flags(0)); // ok but ugly
foo(1); // illegal because of explicit constructor
foo(0); // illegal, but I want to allow this
Is this possible? To allow 0
to be implicitly converted while disallowing other values?
Upvotes: 5
Views: 222
Reputation: 16204
One approach:
Add a constructor which takes void*
.
Since literal 0
is implicitly convertible to a void*
null pointer, and literal 1
isn`t, this will give the desired behavior indicated. For safety you can assert that the pointer is null in the ctor.
A drawback is that now your class is constructible from anything implicitly convertible to void *
. Some unexpected things are so convertible -- for instance prior to C++11, std::stringstream
was convertible to void*
, basically as a hack because explicit operator bool
did not exist yet.
But, this may work out fine in your project as long as you are aware of the potential pitfalls.
Edit:
Actually, I remembered a way to make this safer. Instead of void*
use a pointer to a private type.
It might look like this:
class Flags {
private:
struct dummy {};
public:
Flags (dummy* d) { ... }
...
};
The literal 0
conversion will still work, and it's significantly harder for some user-defined type to accidentally convert to Flags::dummy *
unintentionally.
Upvotes: 6