Тип '?:', Если первый операнд является константным выражением

Рассмотрим следующий код:

void f(float x)
{
    x * (true ? 1.f : 0.0);
}

Тип declval(bool) ? declval(float) : declval(double) равен double в соответствии со стандартом С++ [expr.cond].

Означает ли это, что приведенный выше код должен быть эквивалентен:

void f(float x)
{
    double(x) * 1.0;
} 

Или существует инструкция, которая позволяет оптимизировать, если первый операнд ?: является выражением постоянной времени компиляции?

Ответы

Ответ 1

Компилятор С++ может оптимизировать по своему усмотрению при условии, что он не изменит "наблюдаемое поведение" соответствующей программы (& sect; 1.9p1, так называемое правило "как будто" ).

Например, если на данной платформе известно, что умножение на 1.0 является преобразованием идентичности без возможности ловушки, то умножение фактически не нужно выполнять. (Это может быть или не быть истинным для данной архитектуры, поскольку возможно, что перемножение значения NaN на 1.0 может ловушку. Однако компилятор также может заменить умножение на любую другую операцию, которая создавала бы одну и ту же ловушку при тех же обстоятельствах.)

В отсутствие ловушек и при условии, что умножение на 1.0 является преобразованием идентичности, весь элемент вашей функции f может быть устранен, поскольку стандарт требует, чтобы набор значений float являлся подмножеством множества значений double (возможно, одного и того же набора). Следовательно, поплавок → double- > float round trip должен возвращаться к исходному значению или ловушке. (& sect; 3.9.1p8: "Набор значений типа float является подмножеством набора значений типа double". & sect; 4.8p1: "Значение значения типа с плавающей запятой может быть преобразовано в prvalue другого типа с плавающей точкой. Если исходное значение может быть точно представлено в типе назначения, результатом преобразования является точное представление." )

Итак, да, оптимизация может быть возможна. Но это не влияет на тип выражения ?: в том случае, если тип является наблюдаемым (например, если выражение должно использоваться для вычитания шаблона или как операнд decltype).

Ответ 2

Да, это означает, что вышеуказанные коды эквивалентны.

Используя RTTI, мы можем проверить, что как минимум clang и g++ являются стандартными, и дают d (например, double) как выход для этой программы:

#include <iostream>
#include <typeinfo>

int main() {
    float x = 3.;
    auto val = x * (true ? 1.f : 0.0);
    std::cout << typeid(val).name() << std::endl;
}

И альтернативный способ использования признаков С++ 11

#include <iostream>
#include <typeinfo>

int main() {
    float x = 3.;
    auto val = x * (true ? 1.f : 0.0);
    std::cout << std::boolalpha <<
        std::is_same<decltype(val), double>::value << std::endl;
}

Выходы true.