Если я скомпилирую его на разных компиляторах, я получаю различные результаты. С Clang 3.4 и GCC 4.4.7 он печатает true
, а Visual Studio 2013 печатает false
, что означает, что они вызывают разные операторы трансляции в (bool)b
. Какое правильное поведение соответствует стандарту?
Ответ 1
Стандартные состояния:
Функция преобразования в производном классе не скрывает функцию преобразования в базовом классе, если две функции не преобразуются в один и тот же тип.
§12.3 [class.conv]
Это означает, что operator bool
не скрывается operator int
.
Стандартные состояния:
Во время разрешения перегрузки подразумеваемый аргумент объекта неотличим от других аргументов.
§13.3.3.1 [over.match.funcs]
"подразумеваемый аргумент объекта" в этом случае равен b
, который имеет тип B2 &
. operator bool
требует const B2 &
, поэтому компилятору нужно будет добавить const в b
для вызова operator bool
. Это - при прочих равных условиях - делает operator int
лучшим совпадением.
В стандарте указано, что a static_cast
(который выполняется в C-стиле в этом экземпляре) может преобразовать в тип T
(в данном случае int
), если:
декларация T t(e);
является корректной, для некоторой изобретенной временной переменной T
.
§5.2.9 [expr.static.cast]
Следовательно, int
может быть преобразован в bool
, а a bool
можно в равной степени преобразовать в bool
.
Стандартные состояния:
Рассматриваются функции преобразования S
и его базовые классы. Те функции неявного преобразования, которые не скрыты в S
и тип yield T
, или тип, который может быть преобразован в тип T
через стандартную последовательность преобразования, являются кандидатными функциями.
§13.3.1.5 [over.match.conv]
Таким образом, набор перегрузки состоит из operator int
и operator bool
. При прочих равных условиях operator int
лучше сочетается (поскольку вам не нужно добавлять константу). Поэтому следует выбрать operator int
.
Обратите внимание, что (возможно, против интуиции) стандарт не учитывает тип возврата (т.е. тип, к которому эти операторы конвертируют), как только они были добавлены в набор перегрузки (как установлено выше), при условии, что последовательность преобразований для аргументов одного из них превосходит последовательность преобразования для аргументов другой (которая из-за константы имеет место в этом случае).
Стандартные состояния:
Учитывая эти определения, жизнеспособная функция F1 определяется как лучшая функция, чем другая жизнеспособная функция F2, если для всех аргументов я ICSi (F1) не является худшей последовательностью преобразования, чем ICSi (F2), а затем
- для некоторого аргумента j, ICSj (F1) является лучшей последовательностью преобразования, чем ICSj (F2), или, если не это,
- контекст представляет собой инициализацию по пользовательскому преобразованию и стандартную последовательность преобразования из возвращаемого типа F1 в тип назначения (т.е. тип инициализируемого объекта) является лучшей последовательностью преобразования, чем стандартная последовательность преобразования из тип возврата F2 к типу назначения.
§13.3.3 [over.match.best]
В этом случае существует только один аргумент (неявный this
параметр). Последовательность преобразования для B2 &
= > B2 &
(для вызова operator int
) превосходит B2 &
= > const B2 &
(для вызова operator bool
), и поэтому operator int
выбирается из набора перегрузки без что он фактически не преобразуется непосредственно в bool
.
Ответ 2
Короткие
Функция преобразования operator int()
выбирается clang над operator bool() const
, так как b
не является константной, тогда как оператор преобразования для bool является.
Короткий аргумент состоит в том, что кандидат выполняет функции разрешения перегрузки (с неявным параметром объекта), при преобразовании b
в bool
используются
operator bool (B2 const &);
operator int (B2 &);
где второе - лучшее совпадение, так как b
не является константным.
Если обе функции имеют одну и ту же квалификацию (либо const
, либо нет), выбирается operator bool
, поскольку она обеспечивает прямое преобразование.
Преобразование с помощью буквенных обозначений, шаг за шагом проанализировано
Если мы согласны с тем, что булевский вставщик ввода (std:: basic_ostream:: operator < < (bool val) в соответствии с [ostream.inserters.arithmetic]) вызывается со значением, которое является результатом преобразования b
до bool
мы можем преобразовать это преобразование.
1. Выраженное выражение
Листинг b для bool
(bool)b
оценивается как
static_cast<bool>(b)
согласно С++ 11, 5.4/4 [expr.cast], поскольку const_cast
неприменим (не добавляя или не удаляя здесь const).
Это статическое преобразование разрешено для С++ 11, 5.2.9/4 [expr.static.cast], если bool t(b);
для изобретенной переменной t хорошо сформирована.
Такие операторы называются прямой инициализацией в соответствии с С++ 11, 8.5/15 [dcl.init].
2. Прямая инициализация bool t(b);
Пункт 16 наименее упомянутых стандартных положений пункта (внимание мое):
Семантика инициализаторов такова. Тип назначения - тип инициализированного объекта или ссылки, а тип источника - тип выражения инициализатора.
[...]
[...], если тип источника является классом класса (возможно, с квалификацией cv), рассматриваются функции преобразования.
Применяемые функции преобразования перечисляются, а лучший выбирается с помощью разрешения перегрузки.
2.1 Какие функции преобразования доступны?
Доступные функции преобразования operator int ()
и operator bool() const
, так как С++ 11, 12.3/5 [class.conv] сообщает нам:
Функция преобразования в производном классе не скрывает функцию преобразования в базовом классе, если две функции не преобразуются в один и тот же тип.
В то время как С++ 11, 13.3.1.5/1 [over.match.conv] указывает:
Рассматриваются функции преобразования S и его базовые классы.
где S - класс, который будет преобразован из.
2.2 Какие функции преобразования применимы?
С++ 11, 13.3.1.5/1 [over.match.conv] (выделено мной):
1 [...] Предполагая, что "cv1 T" является типом инициализированного объекта, а "cv S" является типом выражения инициализатора, с S-типом класса, кандидатные функции выбираются следующим образом: Рассматриваются функции преобразования S и его базовые классы. Неявные функции преобразования, которые не скрыты внутри S и тип вывода T , или тип, который может быть преобразован в тип T с помощью стандартной последовательности преобразования, являются кандидатными функциями.
Следовательно, operator bool () const
применим, поскольку он не скрыт внутри B2
и дает bool
.
Часть с акцентом в последней стандартной цитате применима для преобразования с использованием operator int ()
, поскольку int
- это тип, который можно преобразовать в bool через стандартную последовательность преобразования.
Преобразование из int
в bool
- это даже не последовательность, а простое прямое преобразование, разрешенное для С++ 11, 4.12/1 [conv.bool]
Значение арифметики, неперечисленное перечисление, указатель или указатель на тип члена может быть преобразовано в prvalue типа bool. Значение нуля, значение нулевого указателя или значение указателя нулевого элемента преобразуется в значение false; любое другое значение преобразуется в значение true.
Это означает, что operator int ()
также применим.
2.3 Какая функция преобразования выбрана?
Выбор соответствующей функции преобразования выполняется с помощью разрешения перегрузки (С++ 11, 13.3.1.5/1 [over.match.conv]):
Разрешение перегрузки используется для выбора функции преобразования, которую нужно вызвать.
Существует одна специальная "причуда", когда речь идет о разрешении перегрузки для функций-членов класса: параметр неявного объекта ".
Per С++ 11, 13.3.1 [over.match.funcs],
[...] как статические, так и нестатические функции-члены имеют неявный параметр объекта [...]
где тип этого параметра для нестатических функций-членов - согласно пункту 4:
-
"lvalue reference to cv X" для функций, объявленных без ref-qualifier или с рефлектором-определителем
-
"rvalue reference to cv X" для функций, объявленных с помощью && & & реф-классификатор
где X - класс, членом которого является член, а cv - это cv-квалификация в объявлении функции-члена.
Это означает, что (за С++ 11, 13.3.1.5/2 [over.match.conv]), при инициализации с помощью функции преобразования,
[t] список аргументов имеет один аргумент, который является выражением инициализатора. [Примечание. Этот аргумент будет сравниваться с неявным параметром объекта функций преобразования. -end note]
Функции-кандидаты для разрешения перегрузки:
operator bool (B2 const &);
operator int (B2 &);
Очевидно, что operator int ()
является лучшим совпадением, если запрашивается преобразование с использованием непостоянного объекта типа B2
, так как operator bool ()
требуется преобразование квалификации.
Если обе функции преобразования имеют одну и ту же константную квалификацию, разрешение перегрузки этой функции больше не будет делать трюк.
В этом случае начинается преобразование (последовательность).
3. Почему operator bool ()
выбран, когда обе функции преобразования имеют одну и ту же константную квалификацию?
Преобразование из B2
в bool
является пользовательской последовательностью преобразования (С++ 11, 13.3.3.1.2/1 [over.ics.user])
Пользовательская последовательность преобразования состоит из начальной стандартной последовательности преобразования, за которой следует пользовательское преобразование, за которым следует вторая стандартная последовательность преобразования.
[...] Если определяемое пользователем преобразование задается функцией преобразования, начальная стандартная последовательность преобразования преобразует тип источника в неявный объектный параметр функции преобразования.
С++ 11, 13.3.3.2/3 [over.ics.rank]
[...] определяет частичный порядок последовательности неявных преобразований, основанный на улучшенной последовательности преобразований и улучшении преобразования.
[...] Пользовательская последовательность U1 преобразования является лучшей последовательностью преобразования, чем другая пользовательская последовательность U2 преобразования, если они содержат одну и ту же пользовательскую функцию преобразования или конструктор или инициализацию агрегата, а вторую стандартную последовательность преобразования U1 лучше, чем вторая стандартная последовательность преобразования U2.
Второе стандартное преобразование - это случай operator bool()
составляет от bool
до bool
(преобразование идентичности), тогда как второе стандартное преобразование в случае operator int ()
составляет от int
до bool
, которое является булевым преобразованием.
Следовательно, последовательность преобразования, используя operator bool ()
, лучше, если обе функции преобразования имеют одну и ту же константную квалификацию.