Проблема, с которой вы сталкиваетесь (помимо operator std::string()
возврата bool), заключается в том, что неявные преобразования инициируются, когда вы хотите, а когда нет.
Когда компилятор видит s = t
, он определяет следующие потенциальные совпадения std::operator=
:
// using std::string for compactness instead of the full template
std::string::operator=( std::string const & );
std::string::operator=( char );
Теперь t
не является ни одним из них, поэтому он пытается преобразовать его во что-то подходящее и находит два пути: преобразовать в bool, который может быть повышен до char
, или преобразовать в std::string
напрямую. Компилятор не может действительно решить и сдается.
Это одна из причин, по которой вы хотите избежать предоставления множества различных операторов преобразования. Все, что может быть неявно вызвано компилятором, будет в конечном итоге вызвано, когда вы не думаете, что это должно.
Эта статья специально посвящена этой проблеме. Предложение вместо предоставления преобразования в bool
, обеспечивает преобразование в функцию-член
class testable {
typedef void (testable::*bool_type)();
void auxiliar_function_for_true_value() {}
public:
operator bool_type() const {
return condition() ? &testable::auxiliar_function_for_true_value : 0;
}
bool condition() const;
};
Если экземпляр этого класса используется внутри условия (if (testable())
), компилятор попытается преобразовать в bool_type
, который можно использовать в условии.
EDIT
После комментария о том, как код является более сложным с этим решением, вы всегда можете предоставить его как обычную небольшую утилиту. Как только вы предоставите первую часть кода, сложность будет заключена в заголовок.
// utility header safe_bool.hpp
class safe_bool_t;
typedef void (safe_bool_t::*bool_type)();
inline bool_type safe_bool(bool);
class safe_bool_t {
void auxiliar_function_for_true_value() {}
friend bool_type safe_bool(bool);
};
inline bool_type safe_bool(bool)
{
return condition ? &safe_bool_t::auxiliar_function_for_true_value : 0;
}
Ваш класс теперь стал намного проще и сам по себе читаем (выбирая подходящие имена для функций и типов):
// each class with conversion
class testable {
public:
operator bool_type() {
return safe_bool(true);
}
};
Только если читатель заинтересован в том, чтобы узнать, как реализована идиома safe_bool
, и прочитает заголовок, который он заполняет, столкнется со сложностью (которую можно объяснить в комментариях)