Зачем переключаться и если операторы ведут себя по-разному с операторами преобразования?

Почему switch а также if операторы ведут себя по-разному с операторами преобразования?

struct WrapperA
{
explicit operator bool() { return false; }
};

struct WrapperB
{
explicit operator int() { return 0; }
};

int main()
{
WrapperA wrapper_a;
if (wrapper_a) { /** this line compiles **/ }

WrapperB wrapper_b;
switch (wrapper_b) { /** this line does NOT compile **/ }
}

Ошибка компиляции switch quantity is not an integer в то время как в if Заявление это прекрасно признается как bool, (ССЗ)

15

Решение

Синтаксис switch ( condition ) statement с

условие — любое выражение целочисленного типа или типа перечисления, или типа класса контекстуально косвенным образом преобразуемый в целочисленный тип или тип перечисления, или объявление единственной переменной, не являющейся массивом, такого типа с инициализатором скобки или равенства.

Взято из cppreference.

Это означает, что вы можете сделать регистр переключения только для целого числа или перечисления тип. Чтобы компилятор мог неявно конвертировать Wrapper в тип integer / enum, вам нужно удалить явное ключевое слово:

Явный спецификатор указывает, что конструктор или функция преобразования (начиная с C ++ 11) не допускают неявные преобразования

Вы также можете привести Wrapper к типу int.

Изменить по адресу @ acraig5075 примечания:

Вы должны быть осторожны, какой оператор явный, а какой неявный. Если оба неявных, код не будет компилироваться, потому что будет противоречивость:

struct Wrapper
{
operator int() { return 0; }
operator bool() { return true; }
};

source_file.cpp: в функции int main (): source_file.cpp: 12: 14:

ошибка: неоднозначное преобразование типов по умолчанию из «Обертки»

switch (w) {

^ source_file.cpp: 12: 14: примечание: преобразование кандидатов

включают ‘Wrapper :: operator int ()’ и ‘Wrapper :: operator bool ()’

Единственный способ устранить неоднозначность — это сыграть роль.

Если только один из операторов является явным, для оператора switch будет выбран другой:

#include <iostream>
struct Wrapper
{
explicit operator int() { return 0; }
operator bool() { return true; }
};

int main()
{
Wrapper w;
if (w) { /** this line compiles **/std::cout << " if is true " << std::endl; }
switch (w) {
case 0:
std::cout << "case 0" << std::endl;
break;
case 1:
std::cout << "case 1" << std::endl;
break;
}
return 0;
}

Выход :

 if is true
case 1

w был неявно преобразован в 1 (true) (потому что оператор int является явным) и случай 1 выполняется.

С другой стороны :

struct Wrapper
{
operator int() { return 0; }
explicit operator bool() { return true; }
};

Ouput:

 if is true
case 0

w был неявно преобразован в 0 потому что оператор bool явный.

В обоих случаях утверждение if верно, потому что w оценивается контекстуально к логическому значению внутри оператора if.

17

Другие решения

Я думаю этот объясняет, почему switch заявление не принято, тогда как if утверждение является:

В следующих пяти контекстах ожидается тип bool и последовательность неявного преобразования создается, если объявление bool t(e); хорошо сформирован. то есть явная пользовательская функция преобразования, такая как explicit T::operator bool() const; Считается. Такое выражение е называется контекстно конвертируется в bool.

  • управление выражением if, while, for;
  • логические операторы!, && и ||
  • условный оператор?:;
  • static_assert;
  • noexcept.
10

Объявление оператора конвертации explicit существует для предотвращения неявных преобразований в этот тип. Это его цель. switch пытается неявно преобразовать свой аргумент в целое число; поэтому explicit Оператор не будет вызываться. Это ожидаемое поведение.

Что является неожиданным, так это то, что explicit Оператор вызывается в if дело. И тем самым висит сказка.

Смотрите, учитывая приведенные выше правила, способ сделать тип тестируемым через if это сделать неexplicit преобразование в bool, Дело в том… bool это проблемный тип. Он неявно конвертируется в целые числа. Так что если вы делаете тип, который неявно преобразуется в bool, это юридический код:

void foo(int);
foo(convertible_type{});

Но это тоже бессмысленный код. Ты никогда не хотел convertible_type неявно преобразовать в целое число. Вы просто хотели преобразовать его в логическое значение для тестирования.

До C ++ 11, способ исправить это был «безопасный булевский идиом», который был сложной болью и не имел никакого логического смысла (в основном, вы предоставили неявное преобразование в указатель на член, который был преобразован в логическое значение, но не на целые числа или обычные указатели).

Так в C ++ 11, когда они добавили explicit операторы преобразования, они сделали исключение для bool, Если у вас есть explicit operator bool()этот тип может быть «контекстно преобразован в bool«внутри заданного количества определенных языком мест. Это позволяет explicit operator bool() означать «тестируемый в булевых условиях».

switch не нуждается в такой защите. Если вы хотите, чтобы тип был неявно преобразован в int за switch целей, нет никаких причин, по которым он не будет неявно преобразован в int и для других целей.

3

Один ответ таков: if а также switch вести себя по-другому, потому что так был написан стандарт. Другой ответ может дать предположение о том, почему стандарт был написан таким образом. Ну, я полагаю, стандарт сделал if заявления ведут себя таким образом, чтобы решить конкретную проблему (неявные преобразования в bool было проблематично), но я бы хотел принять другую точку зрения.

В if утверждение, условие должно быть логическим значением. Не все думают о if заявления таким образом, предположительно из-за различных удобств, встроенных в язык. Тем не менее, по своей сути, if утверждение должно знать «делай это» или «делай то»; «да или нет»; true или же false — то есть логическое значение. В этом отношении, помещая что-то в условный оператор, явно запрашивается преобразование чего-либо в bool,

С другой стороны, switch Оператор принимает любой целочисленный тип. То есть, нет единого предпочтительного типа по сравнению с другими. Использование switch можно рассматривать как явный запрос на преобразование значения в целочисленный тип, но необязательно специально для int, Поэтому нецелесообразно использовать преобразование в int когда это конкретное преобразование должно быть явно запрошено.

3

Есть две проблемы с вашим кодом.
Во-первых, операторы преобразования не должны быть явными для работы с switch заявления.
Во-вторых, switch условие требует целочисленного типа и оба int а также bool Есть такие типы, так что есть двусмысленность.
Если вы измените свой класс так, чтобы у него не было этих двух проблем, switch скомпилирует и будет работать как положено.

Другое решение, которое не требует изменения вашего класса, это явное приведение (static_cast будет делать) значение для int или же bool,

0

Я полагаю, что истинная причина такого поведения коренится в C, но прежде чем объяснить это, я попытаюсь объяснить это в терминах C ++.

if/while/for оператор должен принимать любой скаляр (целое число, число с плавающей точкой или указатель) или экземпляр класса, конвертируемый в bool, Он просто проверяет, равно ли значение нулю. Учитывая эту вседозволенность, для компилятора относительно безопасно использовать explicit оператор, чтобы вписать значение в if заявление.

switchс другой стороны, имеет смысл только с целыми числами и enums. Если вы попытаетесь использовать switch с double или указатель, вы получите ту же ошибку. Это облегчает определение значений отдельных случаев. Так как switch В операторе особенно необходимо видеть целое число, вероятно, было бы ошибкой использовать экземпляр класса, который не определяет неявное преобразование.

Исторически причина в том, что Вот как С это делает.

Изначально C ++ был предназначен для обратной совместимости с C (хотя в этом он никогда не был успешным). C никогда не имел логического типа до более поздних времен. C о if заявления должны были быть разрешительными, потому что просто не было другого способа сделать это. В то время как C не выполняет преобразования типа структура в скаляр, как это делает C ++, C ++ рассматривает explicit методы отражают тот факт, что if заявления очень разрешительны на обоих языках.

C о switch утверждение, в отличие от if, должен работать с дискретными значениями, поэтому он не может быть таким разрешающим.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector