Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it compiler bug or my bug when using boost::tribool in a conditional?

Tags:

c++

The code is following: (Coliru Code)

#include <stdlib.h>
#include <iostream>
#include <iomanip>
#include <boost/logic/tribool.hpp>

struct B
{
  boost::tribool boo;

  void bug ()
  {
    bool tmp = indeterminate (boo);
    std::cout << "tmp = " << std::boolalpha << tmp << "\n";
    if (tmp && (boo = should_not_be_called ()) == false) {;}
  }

  bool should_not_be_called () const
  {
    std::cout << "BUG, wrong call\n";
    abort ();
  }
};

int main ()
{
  B a;
  a.bug ();
}

The output is

tmp = false
BUG, wrong call
bash: line 7: 14410 Aborted                 (core dumped) ./a.out

I cannot understand why should_not_be_called is called here. The tested compilers was gcc 4.9 and clang 3.6.

UPDATE:

I read the answers and changed the line with "if" to

if (tmp && (false == (boo = should_not_be_called ()))) {;}

(Coliru)

Now there are plain bool types on both sides of && operator, but I still got the same error. Why?

like image 370
Nikki Chumakov Avatar asked Dec 01 '14 19:12

Nikki Chumakov


1 Answers

The compiler is in the right. Let's analyse the types involved in your two ifs, taking into account all the operator overloads that boost::tribool provides:

if (tmp && (boo = should_not_be_called ()) == false) {;}

if (bool && (tribool = bool) == bool) {;} // = is overloaded to return tribool
if (bool && tribool == bool) {;} // == is overloaded to return tribool
if (bool && tribool) {;} // && is overloaded

And the second if:

if (tmp && (false == (boo = should_not_be_called ()))) {;}

if (bool && (bool == (tribool = bool))) {;} // = is overloaded to return tribool
if (bool && (bool == tribool)) {;} // == is overloaded to return tribool
if (bool && tribool) {;} // && is overloaded

In both cases, we end up with an overloaded operator &&. Operator overloads are functions which do not respect special calling behaviour of the built-in operators. That is, user-overloaded &&, || do not short-circuit, and user overloads of , do not guarantee operand evaluation order. All three evaluate all of their operands, in unspecified order, just like any other function call.

This is precisely the reason why it is strongly discouraged to overload &&, ||, or ,, if you want them to mean something like "and", "or" and "sequence."


Excerpt addressing original text of the question:

The compiler is in the right. boost::tribool overloads opeator !, which means the types of the operands of && are bool and boost::tribool. boost::tribool also overloads operator && for these argument types, so this overload gets called.

like image 95
Angew is no longer proud of SO Avatar answered Oct 05 '22 23:10

Angew is no longer proud of SO