Почему этот условный оператор оценивается как int? - PullRequest
2 голосов
/ 27 июня 2019

Я наткнулся на странное (для меня) поведение.Вот мой код:

struct A 
{
  operator unsigned long long() const { return 1ull << 32; }
};

A a1;
unsigned long long a2 = 1ull << 32;

bool b = rand() % 2;
auto c1 = b ? a1 : 0;
auto c2 = b ? a2 : 0;

Почему c1 типа int, а не unsigned long long, как c2?И почему не генерируется предупреждение о преобразовании (VC ++)?

Мне потребовался день, чтобы понять, что не так с моим приложением.

1 Ответ

1 голос
/ 29 июня 2019

Кажется, это соответствует стандарту C ++.

Стандарт C ++ 17

Из проекта стандарта C ++ 17 *, раздел 8.16:

Если второй или третий операнд имеет тип void, [...] В противном случае, если второй и третий операнд являются битовыми полями glvalue той же категории значений, [...]

В противном случае, если второй и третий операнды имеют разные типы и имеют либо (возможно, cv-квалифицированный) тип класса, [...] делается попытка сформировать неявную последовательность преобразования (16.3.3.1) от каждого из этих операндов к типу другого.[...] Предпринимаются попытки сформировать неявную последовательность преобразования из выражения операнда E1 типа T1 в целевой тип, связанный с типом T2 выражения операнда E2, следующим образом

  1. Если E2 является lvalue, типом назначения является «lvalue reference to T2», при условии ограничения, что в преобразовании ссылка должна привязываться непосредственно к lvalue
  2. Если E2 - это xvalue, [...]
  3. Если E2 - это prvalue или если ни одна из приведенных выше последовательностей преобразования не может быть сформирована и хотя бы один из операндов имеет (возможно, cv-квалифицированный) классtype
    1. Если T1 и T2 относятся к одному и тому же типу класса (игнорируя квалификацию cv), [...]
    2. в противном случае тип назначения - это тип, который E2после применения стандартных преобразований lvalue-to-rvalue, array-to-pointer и function-to-pointer.

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

Если второйи третьи операнды являются glvalues ​​одной и той же категории значений и имеют один и тот же тип, [...] В противном случае результатом является значение prvalue.Если второй и третий операнды не имеют один и тот же тип и оба имеют (возможно, cv-квалифицированный) тип класса, [...]

Lvalue-to-rvalue (7.1), array-в стандартный указатель преобразования (7.2) и преобразование функции в указатель (7.3) выполняются второй и третий операнды.После этих преобразований должно выполняться одно из следующего:

  1. Второй и третий операнды имеют одинаковый тип;[...]
  2. Второй и третий операнды имеют арифметический или перечислимый тип;обычные арифметические преобразования выполняются, чтобы привести их к общему типу, и результат этого типа

[...]

Первый случай:

A a1;
auto c1 = b ? a1 : 0;
  • 0 - это значение типа int
  • a1 - это значение типа A

Применяется правило 4:

  • E1 = 0 до E2 = a1: попытка применить правило 4.1.Тип цели A&.Однако неявное преобразование из int в lvalue A&.
  • E1 = a1 в E2 = 0: применяется правило 4.3.2.Тип цели int.Существует неявное преобразование с использованием A::operator unsigned long long().

Поэтому, следуя правилу 4, тип возвращаемого значения этого условного выражения будет int.

Второй случай:

unsigned long long a2 = 1ull << 32;
auto c2 = b ? a2 : 0;
  • 0 является prvalue типа int
  • a2 является lvalue типа unsigned long long

Применяется правило 7.2:Тип возврата условного оператора определяется правилами арифметического преобразования двух выражений.Вот оно unsigned long long.

О предупреждениях компилятора

И clang ( demo ), и g ++ ( demo ) выдают предупреждение с опцией -Wconversion.У меня нет опыта работы с MSVC, но в нем также есть предупреждения об опасных арифметических преобразованиях: C4242 , C4365 .Убедитесь, что они включены.

...