Pregunta "Constexpr if" versus "if" con optimizaciones, ¿por qué se necesita "constexpr"?


C ++ 1z introducirá "constexpr if" - y si tendrá una de las ramas eliminadas, en función de la condición. Parece razonable y útil.

Sin embargo, ¿no es posible prescindir de la palabra clave constexpr? Creo que durante la compilación, el compilador debe saber si la condición es conocida durante el tiempo de compilación o no. Si lo es, incluso el nivel de optimización más básico debería eliminar una rama innecesaria.

Por ejemplo (ver en godbolt: https://godbolt.org/g/IpY5y5)

int test() {
    const bool condition = true;
    if (condition) {
      return 0;
    } else {
      // optimized out even without "constexpr if"
      return 1;
    }
}

Godbolt explorer muestra que incluso gcc-4.4.7 con -O0 no compiló "return 1", por lo que logró lo prometido con constexpr if. Obviamente, un compilador tan antiguo no podrá hacerlo cuando la condición sea resultado de la función constexpr, pero el hecho sigue siendo válido: el compilador moderno sabe si la condición es constesta o no y no necesita que le diga explícitamente.

Entonces la pregunta es:

¿Por qué se necesita "constexpr" en "constexpr if"?


32
2017-12-05 10:59


origen


Respuestas:


Esto es fácil de explicar a través de un ejemplo. Considerar

struct Cat { void meow() { } };
struct Dog { void bark() { } };

y

template <typename T>
void pet(T x)
{
    if(std::is_same<T, Cat>{}){ x.meow(); }
    else if(std::is_same<T, Dog>{}){ x.bark(); }
}

Invocando

pet(Cat{});
pet(Dog{});

activará un error de compilación (ejemplo de wandbox), porque ambas ramas de la if declaración debe estar bien formada.

prog.cc:10:40: error: no member named 'bark' in 'Cat'
    else if(std::is_same<T, Dog>{}){ x.bark(); }
                                     ~ ^
prog.cc:15:5: note: in instantiation of function template specialization 'pet<Cat>' requested here
    pet(Cat{});
    ^
prog.cc:9:35: error: no member named 'meow' in 'Dog'
    if(std::is_same<T, Cat>{}){ x.meow(); }
                                ~ ^
prog.cc:16:5: note: in instantiation of function template specialization 'pet<Dog>' requested here
    pet(Dog{});
    ^

Cambiando pet usar if constexpr

template <typename T>
void pet(T x)
{
    if constexpr(std::is_same<T, Cat>{}){ x.meow(); }
    else if constexpr(std::is_same<T, Dog>{}){ x.bark(); }
}

solo requiere que las ramas sean analizables; solo la rama que coincide con la condición debe estar bien formada (ejemplo de wandbox).

El fragmento

pet(Cat{});
pet(Dog{});

compilará y trabajará como se espera.


41
2017-12-05 11:07