Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you evaluate a constructor call to boolean with an overloaded bool()?

Tags:

c++

Can a constructor call be evaluated to a boolean if the bool() operator is overloaded?

class A
{
  public:
  A() {};
  operator bool() const { return true; }
}

main()
{
  if (A a = A())
  {
  // do stuff
  }
}

Is the above code valid, or do I need to implement main like:

int main(int argc, const char* argv[])
{
A a();
if (a)
  {
  // do stuff
  }
}

This code is going to wind up all over the place in my code base, so less lines, increased legibility, and reduced scope are important, and would be improved by this.

Any ideas?

like image 309
Mike Lewis Avatar asked Dec 17 '22 06:12

Mike Lewis


1 Answers

The code contains a few syntactic and semantic bugs. Let's fix them

class A
{
public:
  A() {};
  operator bool() { return true; }
};

int main()
{
  if (A a = A())
  {
    // do stuff
  }
}

You may choose to change the type in the conversion function to something else. As written, the boolean conversion will also succeed to convert to any integer type. Converting to void* will limit conversion to only bool and void*, which is a commonly used idiom. Yet another and better way is to convert to some private type, called safe bool idiom.

class A
{
private:
  struct safe_bool { int true_; };
  typedef int safe_bool::*safe_type;
public:
  A() {};
  operator safe_type() { return &safe_bool::true_; }
};

Back to syntax: If you have an else part, you may use the name of the declared variable, because it's still in scope. It's destroyed after all branches are processed successfully

if(A a = A())
{ ... }
else if(B b = a)
{ ... }

You may also use the same name as before, and the variable will shadow the other variables, but you may not declare the same name in the most outer block of any branch - it will conflict rather than hide with the other declaration.

if(int test = 0)
{ ... }
else
{ int test = 1; /* error! */ }

The technique to declare and initialize a variable is most often used together with dynamic_cast though, but can be perfectly used together with a user defined type like above, too

if(Derived *derived = dynamic_cast<Derived*>(base)) {
  // do stuff
}

Note that syntactically, you have to initialize the variable (using the = expression form like for a default argument). The following is not valid

if(ifstream ifs("file.txt")) {
  // invalid. Syntactic error
}
like image 57
Johannes Schaub - litb Avatar answered May 26 '23 16:05

Johannes Schaub - litb