Вопрос

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 :: оператора double приводится к типу int, а затем обратно к double. Почему?

Это было полезно?

Решение

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

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

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

<Ч>

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

  

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

<Ч>

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

5.16 / 3 о состоянии ? 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), являются функциями-кандидатами.

Другие советы

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

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

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

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

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

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

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

<Ол>
  • Ваш компилятор работает в соответствии со стандартом.

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

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

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

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

    Тернарный оператор

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

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top