Несовместимые типы операндов при использовании тернарного условного оператора

Этот код:

  bool contains = std::find(indexes.begin(), indexes.end(), i) != indexes.end();
  CardAbility* cardAbility = contains ? new CardAbilityBurn(i) : new CardAbilityEmpty;

дает следующую ошибку:

Несовместимые типы операндов CardAbilityBurn и CardAbilityEmpty

Однако, если я напишу код следующим образом:

 if (contains)
 {
    cardAbility = new CardAbilityBurn(i);
 }
 else
 {
    cardAbility = new CardAbilityEmpty;
 }

тогда компилятор не возражает. Почему так? Я хочу использовать тернарный условный оператор, потому что это всего лишь одна строка. Что там?

Мне нужно отметить (я думаю, вам может понадобиться эта информация), что CardAbilityEmpty и CardAbilityBurn оба выводятся из CardAbility, поэтому они так говорят братья.

Спасибо

Ответы

Ответ 1

Система типа С++ определяет типы выражений изнутри out [1]. Это означает, что тип условного выражения определяется до того, как будет выполнено присвоение CardAbility*, и компилятор должен выбрать только CardAbilityBurn* и CardAbilityEmpty*.

Поскольку С++ имеет множественное наследование и несколько более возможных путей преобразования, поскольку ни один из типов не является суперклассом другого, компиляция останавливается там.

Чтобы успешно компилировать, вам необходимо предоставить отсутствующую часть: поместить один или оба операнда в тип базового класса, поэтому условное выражение в целом может принимать этот тип.

auto* cardAbility = contains
    ? static_cast<CardAbility*>(new CardAbilityBurn(i))
    : static_cast<CardAbility*>(new CardAbilityEmpty  );

(Обратите внимание на использование авто, поскольку вы уже указали тип адресата в правильном выражении.)

Однако он немного запутан, поэтому в конце концов структура if - else лучше подходит для этого случая.

[1] Существует одно исключение: перегруженные имена функций не имеют окончательного типа, пока вы их не конвертируете (неявно или явно) в одну из своих версий.

Ответ 2

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

Если оба операнда одного типа, результат этого типа.

Если оба операнда являются арифметическими или перечисляемыми типами, обычные арифметические преобразования (охватываемые арифметическими преобразованиями) выполняются для преобразуйте их в общий тип.

Если оба операнда имеют типы указателей или если один из них является типом указателя и other - это постоянное выражение, которое вычисляется до 0, преобразования указателей для преобразования их в общий тип.

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

Если оба операнда имеют тип void, общий тип - это тип void.

Если оба операнда имеют один и тот же определяемый пользователем тип, общий тип этот тип.

Если операнды имеют разные типы и по крайней мере один из операндов имеет пользовательский тип, тогда правила языка используются для определить общий тип. (См. Предупреждение ниже.)

И тогда есть предостережение:

Если типы второго и третьего операндов не идентичны, то правила преобразования сложных типов, как указано в стандарте С++, являются вызывается. Эти преобразования могут привести к неожиданному поведению, включая строительство и уничтожение временных объектов. По этой причине мы настоятельно рекомендуется либо (1) избегать использования пользовательских типов, как операнды с условным оператором или (2), если вы используете определяемые пользователем, затем явно прикладывать каждый операнд к общему тип.

Вероятно, именно по этой причине Apple отключила это неявное преобразование в LLVM.

Итак, если /else кажется более подходящим в вашем случае.