2

I have some class that can be set or not:

Class obj;

I want its value, when used in logic, to return whether or not it's set:

if ( obj )
    obj.Clear();

if ( !obj )
    obj.Set( "foo" );

I thought of adding in an implicit conversion to bool, but I wonder if int would be necessary or whether there's a better way to go about this.

asmmo
  • 6,922
  • 1
  • 11
  • 25
Swiss Frank
  • 1,985
  • 15
  • 33

3 Answers3

5

You can define a bool operator, as follows

#include <iostream>

struct Object{
    bool state;
    explicit operator bool()const{
        return state;
    }
};
int main(){

    Object o1;
    Object o2;
    o1.state = false;
    o2.state = true;
    std::cout << "\no1 state is " << (o1?  "true": "false");
    std::cout << "\no2 state is " << (!o2?  "false": "true");
}

The output is

o1 state is false
o2 state is true

Live

asmmo
  • 6,922
  • 1
  • 11
  • 25
  • Could you add just a little detail to your kind answer, given an explanation of how the compiler knows to use the bool() operator, and why say int() wouldn't work or wouldn't be better? – Swiss Frank Jul 25 '20 at 19:31
  • That's a good trick! Never though of overriding the `bool` operator – Debargha Roy Jul 25 '20 at 19:32
  • 2
    The `operator bool` should be `explicit`! Otherwise you'll get weird implicit conversions, e.g. `o1 + 42` will compile. – HolyBlackCat Jul 25 '20 at 19:33
  • @HolyBlackCat interesting; do you have time to explain why? That might make a good answer, as opposed to comment. – Swiss Frank Jul 25 '20 at 19:34
  • 1
    @SwissFrank There's not much more to it. Making it `explicit` means it'll only be triggered by a `bool` cast, or in [some sane circumstances](https://stackoverflow.com/a/39995574/2752075) (such as being used as a condition in `if`, or a loop, or `? :`, or as an operand to `! && ||`). If it's not `explicit`, it can be triggered in lame ways, such as the mentioned `o1 + 42`. – HolyBlackCat Jul 25 '20 at 19:39
2

You can use std::optional or std::shared_ptr.

Example 1:

std::optional<Object> obj = Object();

if (obj) obj->useIt();

obj.reset();

if (obj) ... ;

Example 2

std::shared_ptr<Object> obj = new Object();

if (obj) obj->useIt();

obj.reset();

if (obj) ... ;
Ihor Drachuk
  • 1,265
  • 7
  • 17
2

You should represent optional values using std::optional. This is self documenting, other programmers know what they are about, and you avoid checking for invariant states of your own class (reduce complexity). It also uses STL conventions for value wrappers (same as iterators, smart pointers, etc.).

If you don't want to or simply can't use it, look at it's implementation and follow the same rules:

  1. implement a bool-conversion-operator (explicit)
  2. provide a method (like std::optional::has_value()) for use in templated methods

Note that if (...) performs an explicit bool-cast on the statement. So generally there is no need for implicit conversions. In order to avoid trouble using overloads, you should always go for explicit conversion-operators.

local-ninja
  • 1,198
  • 4
  • 11
  • Thanks. The class already has the internal notion of set or not, so don't need that template. I didn't know that `if` has an explicit bool cast. That more or less answers my question right there. What is your point 2.? If you have time could you add an explanation why that's a good idea? – Swiss Frank Jul 25 '20 at 19:37
  • 1
    When you write generic functions, you usually don't rely on the arguments operators. For example, you don't use `operator&` but `std::addressof`, because you don't know the type and if the operator is overloaded. Therefore it's common to provide methods for all operators of a class. – local-ninja Jul 25 '20 at 19:43