c++ language-lawyer c++17 constexpr if-constexpr

c++ - ¿Por qué un if si constexpr hace que este error de expresión constante de núcleo desaparezca?



language-lawyer c++17 (3)

El estándar no dice mucho sobre la declaración descartada de un if constexpr . Hay esencialmente dos afirmaciones en [stmt.if] sobre estas:

  1. En una plantilla adjunta, las sentencias descartadas no se instancian.
  2. Los nombres a los que se hace referencia desde una declaración descartada no requieren ODR para definirse.

Ninguno de estos se aplica a su uso: los compiladores son correctos para quejarse sobre el constexpr caso de inicialización. Tenga en cuenta que deberá hacer que la condición dependa de un parámetro de plantilla cuando desee aprovechar la creación de instancias para fallar: si el valor no depende de un parámetro de plantilla, la falla ocurre cuando se define la plantilla. Por ejemplo, este código sigue fallando:

template <typename T> void f() { constexpr int x = -1; if constexpr (x >= 0){ constexpr int y = 1<<x; } }

Sin embargo, si haces x dependiente del tipo T , está bien, incluso cuando f se crea una instancia con int :

template <typename T> void f() { constexpr T x = -1; if constexpr (x >= 0){ constexpr int y = 1<<x; } } int main() { f<int>(); }

En referencia a esta pregunta . La expresión constante central que se utiliza para inicializar la variable constexpr y está mal formada. Tanto es un hecho.

Pero si trato de convertir el if en un if constexpr :

template <typename T> void foo() { constexpr int x = -1; if constexpr (x >= 0){ constexpr int y = 1 << x; } } int main(){ foo<int>(); }

El error persiste Con GCC 7.2 todavía dando:

error: right operand of shift expression ''(1 << -1)'' is negative [-fpermissive]

Pero pensé que la comprobación semántica debería dejarse sin realizar en una rama descartada.

Hacer una indirección a través de un constexpr lambda ayuda, sin embargo:

template <typename T> void foo(){ constexpr int x = -1; constexpr auto p = []() constexpr { return x; }; if constexpr (x >= 0){ constexpr int y = 1<<p(); } }

El especificador constexpr en y parece alterar cómo se verifica la rama descartada. ¿Es este el comportamiento pretendido?

@ max66 tuvo la amabilidad de comprobar otras implementaciones. Informa que el error es reproducible con GCC (7.2.0 / Head 8.0.0) y Clang (5.0.0 / Head 6.0.0).


No estoy seguro de por qué esperas que no se verifique la rama. La única vez que una rama if "no está marcada" es cuando forma parte de una plantilla y no está instanciada , según [stmt.if] p2:

Durante la creación de instancias de una entidad con plantilla encerrada (Cláusula 17), si la condición no depende del valor después de su creación de instancias, no se crea una instancia de la subestimación descartada (si existe).

Su código no parece estar en una situación en la que esto se aplique.


Tenga en cuenta que para la declaración descartada por Constexpr If :

La declaración descartada no puede estar mal formada por cada especialización posible:

Para solucionar el problema, puede hacer la declaración dependiendo del parámetro de la plantilla, por ejemplo,

template<typename T, int X> struct dependent_value { constexpr static int V = X; }; template <typename T> void foo() { constexpr int x = -1; if constexpr (x >= 0){ constexpr int y = 1 << dependent_value<T, x>::V; } }

LIVE