Является ли это ошибкой компилятора или моей ошибкой при использовании boost:: tribool в условном выражении?

Следующий код: (Код Coliru)

#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 ();
}

Выходной сигнал

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

Я не могу понять, почему this_not_be_called вызывается здесь. Проверенные компиляторы были gcc 4.9 и clang 3.6.

UPDATE:

Я прочитал ответы и сменил строку "if" на

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

(Coliru)

Теперь существуют простые типы bool с обеих сторон && оператора, но я все равно получил ту же ошибку. Почему?

Ответ 1

Компилятор находится справа. Давайте проанализируем типы, связанные с вашими двумя if s, принимая во внимание все операторы которые boost::tribool предоставляет:

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

И второй 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

В обоих случаях мы получаем перегруженный operator &&. Перегрузки операторов - это функции, которые не учитывают поведение специальных вызовов встроенных операторов. То есть пользовательский перегруженный &&, || делает не короткое замыкание, а пользовательские перегрузки , делают не гарантированный порядок оценки операнда. Все три оценивают все их операнды в неуказанном порядке, как и любой другой вызов функции.

Именно по этой причине настоятельно не рекомендуется перегружать &&, || или ,, если вы хотите, чтобы они имели в виду нечто вроде "и", "или" и "последовательность".


Выдержка из оригинального текста вопроса:

Компилятор находится справа. boost::tribool перегрузки opeator !, что означает, что типы операндов && равны bool и boost::tribool. boost::tribool также перегружает operator && для этих типов аргументов, поэтому эта перегрузка вызывается.

Ответ 2

Логические операторы в boost:: tribool перегружены, как упоминалось ЗДЕСЬ, поэтому в отличие от встроенных логических операторов С++ оценка слева и справа логических операторов не применяется и нет короткого замыкания, поэтому оценка операндов остается неопределенной.