Rakurai
Rakurai

Reputation: 1016

Allowing implicit conversion for a single value

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

Answers (1)

Chris Beck
Chris Beck

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

Related Questions