C++ safe bool wrapper
Asked Answered
Z

4

42

I'm trying to design a bool wrapper struct applying the safe bool idiom.
The classic implementation to solve this is pretty trivial: the skeleton could be something like this:

struct Bool final
{
  Bool() = default;

  Bool(bool value)
    : _value{value}
  {}

  explicit operator bool() const {
    return _value;
  }

private:
  bool _value{false};
};

The part I'm trying to improve is how Bool is constructed.
For example I want to avoid implicit narrowing by design:

Bool b1(45); // yields warnings, but it compiles
Bool b2{3};  // not ok by standard

I tried to hurt myself using templates, but without success.

How could I make it work?

Zosema answered 16/1, 2017 at 20:40 Comment(2)
You may also want to look at some prior art regarding safe bool. For example, this page goes through the process of making a safe bool and explaining some of the pitfalls which one can run into along the way (for example, an operator bool comes with some unintended side effects that other cast operators can avoid)Beatrisbeatrisa
Thank you! I'll definitely take a look at it!Zosema
P
57

You can achieve this by explicitly deleting all other constructors.

struct Bool final
{
    template<class T>
    Bool(T) = delete;

    Bool(bool value);
};
Plasia answered 16/1, 2017 at 20:43 Comment(14)
where was the declaration of _value? did you omit that for brevity?Sore
@Sore I omitted everything but the constructors.Zooid
That code will also delete the default constructor (implicitly), right?Pilgrim
@gurka See default constructor. "If no user-declared constructors of any kind are provided for a class type[...], the compiler will always declare a default constructor as an inline public member of its class.". Simply providing Bool(bool value) is enough to delete the default constructor.Zooid
So simple, so elegant.Bookman
is _value global?Rupiah
@Mr.Robot As previous comments indicate, I omitted most of the class for clarity, as it has no bearing on the concept my answer is trying to illustrate. I've edited my answer to remove the body of the constructor in the hopes that it will avoid future confusion.Zooid
Should the ctor taking one parameter be marked as explicit?Jehanna
@Jehanna It could, but this is one case where it might be desirable to omit it. The purpose of OPs class is to substitute the type bool` where it could allow undesirable implicit casts. By leaving the constructor implicit (by omitting explicit) you can swap a use of bool with this Bool class transparently, without any other change. For example, if you added explicit, you couldn't use Bool as a function parameter where you would have used bool without explicitly casting. By omitting it, the change is transparent.Zooid
You could add a constructor that takes Bool to support your scenario and still block implicit conversions from other non-bool types.Jehanna
@FrançoisAndrieux, I used just explicit keyword in constructor and deleted the template constructor and the following is valid without any warnings Bool b1 {true}; Bool b2{b1}; Bool b3{Bool{false}}; Maybe I misunderstood your above comment, but I thought you said this shouldn't work with explicit.Warhead
@Warhead The first two count as explicit construction. You are explicitly saying you want to create an instance of Bool. The third case is a copy, which is a distinct constructor. One example where explicit would come into play is if a function expected a Bool but you gave it a bool. It wouldn't work if the constructor was explicit.Zooid
@FrançoisAndrieux, so basically for the purpose of the OP question, I don't see any gains in using the template constructor against just explicit one. As you mentioned above explicit would come into play if a function expected a Bool and you gave it a bool, but that's not the case in the above class. Am I missing something? Thank you for taking the time to responding!Warhead
@Warhead As you pointed out, explicit won't prevent Bool foo{true};, neither would it prevent Bool foo{1};. This second case is what OP was trying to prevent. By deleting the constructor template, you prevent Bool foo{1};, because it would bind to a deleted constructor.Zooid
H
24

Add, and explicitly delete a template constructor:

template <typename T>
Bool(T) = delete;

It matches anything other than actual bool better than other constructors, and will thus prevent implicit conversion.

Holm answered 16/1, 2017 at 20:44 Comment(1)
"anything other than actual bool" -- and Bool: this doesn't, and probably shouldn't, disable the compiler-generated copy and move constructors.Sluggish
C
18

If you just need:
A variable that is only "true" or "false" and cannot be implicitly converted to int/char/pointer then I would look at using an enum class:

enum class Bool {
    False,
    True,
};
Cessionary answered 17/1, 2017 at 2:12 Comment(0)
E
16

I'm trying to design a bool wrapper struct applying the safe bool idiom.

Don't.

The safe bool idiom is only relevant in C++03 and earlier - where if you express that your type is "truthy" by doing something like:

struct A {
    operator bool() const;
};

you'd run into all sorts of issues like:

A{} + 4;    // ok?!
A{} < 0;    // ok?!
A{} == B{}; // ok if B also has operator bool??!

So the safe bool idiom was a solution to this accidental implicit conversion problem, using function pointers (of course, function pointers!).

In C++11, we have a way better solution:

struct A {
    explicit operator bool() const;
};

which does exactly what we want. In fact, it was literally designed to solve this problem. And while the safe bool idiom is fairly complicated scaffolding, explicit operator bool is super straightforward to use and just does the Right Thing. You don't need a wrapper for it - it's actually harder to use your wrapper than to write the explicit operator bool directly.

Moreover, your wrapper imposes on the user (a) non-derivability because you made Bool final and (b) an extra bool member, that you have to keep in sync, so it introduces rather than solves problems. Consider how much more work it would be for you to implement:

template <class T>
struct my_unique_ptr : Bool { ... };

vs

template <class T>
struct my_unique_ptr {
    T* ptr;

    explicit operator bool() const { return ptr; }
};
Escarole answered 17/1, 2017 at 14:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.