Diferencia entre "if constexpr()" y "if()"

Resuelto msc asked hace 7 años • 2 respuestas

¿ Cuál es la diferencia entre if constexpr()y if()?

¿Dónde y cuándo puedo utilizar ambos?

msc avatar Apr 16 '17 13:04 msc
Aceptado

La única diferencia es que if constexprse evalúa en tiempo de compilación, mientras ifque no. Esto significa que las ramas pueden rechazarse en el momento de la compilación y, por lo tanto, nunca se compilarán.


Imagine que tiene una función lengthque devuelve la longitud de un número o la longitud de un tipo que tiene una .length()función. No puedes hacerlo en una función, el compilador se quejará:

template<typename T>
auto length(const T& value) noexcept {
    if (std::is_integral<T>::value) { // is number
        return value;
       }
    else{
        return value.length();
    }
}

int main() noexcept {
    int a = 5;
    std::string b = "foo";

    std::cout << length(a) << ' ' << length(b) << '\n'; // doesn't compile
}

Mensaje de error:

main.cpp: In instantiation of 'auto length(const T&) [with T = int]':
main.cpp:16:26:   required from here
main.cpp:9:16: error: request for member 'length' in 'val', which is of non-class type 'const int'
     return val.length();
            ~~~~^~~~~~

Esto se debe a que cuando el compilador crea una instancia length, la función se verá así:

auto length(const int& value) noexcept {
    if (std::is_integral<int>::value) { // is number
        return value;
    else
        return value.length();
}

valuees un int, y como tal no tiene una lengthfunción miembro, por lo que el compilador se queja. El compilador no puede ver que nunca se alcanzará la declaración para an int, pero no importa, ya que el compilador no puede garantizarlo.

Ahora puedes especializarte length, pero para muchos tipos (como en este caso, cada número y clase con una lengthfunción miembro), esto da como resultado una gran cantidad de código duplicado. SFINAE también es una solución, pero requiere múltiples definiciones de funciones, lo que hace que el código sea mucho más largo de lo necesario para compararlo con el siguiente.

Usar if constexpren lugar de ifsignifica que la rama ( std::is_integral<T>::value) se evaluará en el momento de la compilación y, si es así, se descartarán truetodas las demás ramas ( else ify ). elseSi es así false, se marca la siguiente rama (aquí else), y si es así true, descarta todas las demás ramas, y así sucesivamente...

template<typename T>
auto length(const T& value) noexcept {
    if constexpr (std::integral<T>::value) { // is number
        return value;
    else
        return value.length();
}

Ahora, cuando el compilador cree una instancia length, se verá así:

int length(const int& value) noexcept {
    //if constexpr (std::is_integral<int>::value) { this branch is taken
        return value;
    //else                           discarded
    //    return value.length();     discarded
}

std::size_t length(const std::string& value) noexcept {
    //if constexpr (std::is_integral<int>::value) { discarded
    //    return value;                   discarded
    //else                           this branch is taken
        return value.length();
}

Entonces esas 2 sobrecargas son válidas y el código se compilará correctamente.

Rakete1111 avatar Apr 16 '2017 07:04 Rakete1111

La declaración ordinaria if:

  • ¿Se evalúa su condición cada vez que el control llega hasta él, si es que alguna vez lo logra?
  • Determina cuál de las dos subsentencias ejecutar, omitiendo la otra.
  • Requiere que ambas subdeclaraciones estén bien formadas independientemente de cuál esté realmente seleccionada en tiempo de ejecución

La if constexprdeclaración:

  • ¿Se ha evaluado su condición en el momento de la compilación una vez que se han proporcionado todos los argumentos de plantilla necesarios?
  • Determina cuál de las dos subdeclaraciones compilar, descartando la otra.
  • No requiere que la subsentencia descartada esté bien formada
Brian Bi avatar Apr 16 '2017 07:04 Brian Bi