Порядок оценки троичного оператора - PullRequest
2 голосов
/ 18 сентября 2009
class Foo {
  public:
  explicit Foo(double item) : x(item) {}

  operator double() {return x*2.0;}

  private:
  double x;
}

double TernaryTest(Foo& item) {
  return some_condition ? item : 0;
}

Foo abc(3.05);
double test = TernaryTest(abc);

В приведенном выше примере, почему проверка равна 6 (вместо 6.1), если some_condition имеет значение true?

Изменение кода, как показано ниже, возвращает значение 6,1

double TernaryTest(Foo& item) {
  return some_condition ? item : 0.0; // note the change from 0 to 0.0
}

Кажется, что (в исходном примере) возвращаемое значение из Foo :: operator double приводится к int, а затем обратно к double. Почему?

Ответы [ 4 ]

9 голосов
/ 18 сентября 2009

Условный оператор проверяет преобразования в обоих направлениях.В этом случае, поскольку ваш конструктор является явным (поэтому ?: не является неоднозначным), используется преобразование из Foo в int с использованием функции преобразования, которая преобразуется в double: это работает, потому что после примененияза функцией преобразования следует стандартное преобразование, которое преобразует double в int (усечение).Результат ?: в вашем случае равен int и имеет значение 6.

Во втором случае, поскольку операнд имеет тип double, такого конечного преобразования в int не происходит, и, таким образом, тип результата ?: имеет тип double с ожидаемым значением.

Чтобы понять «ненужные» преобразования, вы должны понимать, что выражения, подобные вашему ?:, оцениваются как «не зависящие от контекста»: при определении значения и его типа компилятор не считает, что оноперанд return для функции, возвращающей double.


Редактировать: Что произойдет, если ваш конструктор неявный ?Выражение ?: будет неоднозначным, поскольку вы можете преобразовать int в значение типа Foo (с помощью конструктора) и Foo в значение типа int (с помощью функции преобразования).,Стандарт гласит:

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


Параграфы, объясняющие, как ваш Foo преобразуется в int:

5.16/3 about condition ? E1 : E2:

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

4.3 о «неявно преобразованном»:

Выражение e может быть неявно преобразовано в тип T тогда и только тогда, когда объявление T t = e; правильно сформировано, длянекоторая изобретенная временная переменная t.

8.5/14 о инициализации копирования (T t = e;)

Если тип источника является (возможно, cv-квалифицированным) типом класса,функции преобразования рассматриваются.Перечислены применимые функции преобразования (13.3.1.5), и лучшая из них выбирается с помощью разрешения перегрузки (13.3).Выбранное пользователем преобразование, выбранное таким образом, вызывается для преобразования выражения инициализатора в инициализируемый объект.Если преобразование не может быть выполнено или является неоднозначным, инициализация является неправильной.

13.3.1.5 о кандидатах на функцию преобразования

Функции преобразования S и егобазовые классы считаются.Те, которые не скрыты в S и дают тип T или тип, который можно преобразовать в тип T с помощью стандартной последовательности преобразования (13.3.3.1.1), являются функциями-кандидатами.

7 голосов
/ 18 сентября 2009

Это подробно описано в разделе 5.16 стандарта. Важная часть в параграфе 3. «Если E2 является lvalue: E1 может быть преобразован, чтобы соответствовать E2, если E1 может быть неявно преобразован (пункт 4) в тип« ссылка на T2 », при условии ограничения, что в преобразовании ссылка должна быть привязана напрямую (8.5.3) к E1. "

В выражении единственным значением l является item, поэтому вопрос заключается в том, можно ли неявно преобразовать 0 (целое число) в тип Foo. В этом случае не существует неявного преобразования любого другого типа в Foo, поскольку единственная доступная функция преобразования помечена explicit. Следовательно, это не работает, и мы следуем «если E2 является r-значением, или если вышеупомянутое преобразование не может быть выполнено:« (пропуская часть, если у них обоих есть тип класса) «Иначе (то есть, если E1 или E2 имеет неклассный тип, или если они оба имеют типы классов, но базовые классы не совпадают или являются базовыми классами другого): E1 может быть преобразован в соответствие E1, если E1 может быть неявно преобразован в тип, который является выражением E2 было бы, если бы E2 был преобразован в значение r (или тип, который у него есть, если E2 является значением r). "

Следовательно, мы видим, что 0 - это значение типа int. Мы можем преобразовать Foo, поскольку мы можем неявно преобразовать Foo в double и, следовательно, в int. Тогда:

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

Поскольку мы можем преобразовать Foo в int, мы преобразуем Foo в int для оставшейся части определения. Теперь у нас есть два int в качестве типов выражений, и, по крайней мере, один является rvalue.

Я могу перейти к пунктам 5 и 6, но я думаю, что вполне очевидно, что выражение имеет тип int.

Я думаю, что вынос:

  1. Ваш компилятор работает в соответствии со стандартом.

  2. Правила типа условного выражения слишком сложны, чтобы их было легко выучить. Не выдвигайте конверт, потому что вы когда-нибудь совершите ошибку. (Кроме того, это именно то место, где компилятор может не реализовать стандарт точно.)

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

1 голос
/ 18 сентября 2009

Тип троичного выражения определяется во время компиляции; не имеет значения, что представляет собой some_condition во время выполнения.

Наверное, тогда возникает вопрос: почему компилятор выбирает int вместо double в первом примере?

0 голосов
/ 18 сентября 2009

Тернарный оператор угадывает тип по его аргументам. он не может преобразовать элемент в int, но может преобразовать элемент в двойной, который затем преобразуется в int.

...