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

Почему операторы 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.(НКА)

Ответы [ 6 ]

0 голосов
/ 06 июня 2018

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

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

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

Исторически сложилось так, что именно так делает Cit.

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

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

0 голосов
/ 06 июня 2018

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

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

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

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

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

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

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

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

0 голосов
/ 06 июня 2018

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

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

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

0 голосов
/ 06 июня 2018

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

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

0 голосов
/ 06 июня 2018

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

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

  • , управляющее выражением, если, тогда как для;
  • логических операторов!,&& и ||;
  • условный оператор?:;
  • static_assert;
  • noexcept.
0 голосов
/ 06 июня 2018

Синтаксис switch ( condition ) statement с условием

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

Взят из cppreference.

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

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

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

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

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

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

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

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

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; }    
};

Выход:

 if is true 
case 0

w был неявно преобразован в 0, поскольку оператор bool является явным.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...