c++ templates constexpr c++17 static-assert

c++ - constexpr if y static_assert



templates c++17 (2)

P0292R1 constexpr si se ha included , en el camino para C ++ 17. Parece útil (y puede reemplazar el uso de SFINAE), pero un comentario sobre static_assert está mal formado, ningún diagnóstico requerido en la rama falsa me asusta:

Disarming static_assert declarations in the non-taken branch of a constexpr if is not proposed. void f() { if constexpr (false) static_assert(false); // ill-formed } template<class T> void g() { if constexpr (false) static_assert(false); // ill-formed; no // diagnostic required for template definition }

Considero que está completamente prohibido usar static_assert dentro de constexpr if (al menos la rama falsa / no tomada, pero que en la práctica significa que no es seguro ni útil hacerlo).

¿Cómo surge esto del texto estándar? No encuentro mención de static_assert en la redacción de la propuesta, y las funciones constexpr C ++ 14 permiten static_assert (detalles en cppreference: constexpr ).

¿Se está escondiendo en esta nueva oración (después de 6.4.1)? :

Cuando aparece una declaración constexpr if en una entidad con plantilla, durante una instanciación de la plantilla adjunta o lambda genérica, una declaración descartada no se crea una instancia.

A partir de ahí, supongo que también está prohibido, no se requiere diagnóstico, llamar a otras funciones constexpr (plantilla) que en algún lugar del gráfico de llamadas pueden llamar static_assert .

Línea de fondo:

Si mi comprensión es correcta, ¿no pone eso un límite bastante duro a la seguridad y la utilidad de constexpr if como deberíamos saber (a partir de la documentación o la inspección del código) sobre cualquier uso de static_assert ? ¿Mis preocupaciones están fuera de lugar?

Actualizar:

Este código compila sin previo aviso (clang head 3.9.0) pero, a mi entender, está mal formado , no requiere diagnóstico. Válido o no?

template< typename T> constexpr void other_library_foo(){ static_assert(std::is_same<T,int>::value); } template<class T> void g() { if constexpr (false) other_library_foo<T>(); } int main(){ g<float>(); g<int>(); }


Se trata de una regla bien establecida para plantillas: la misma regla que permite a los compiladores diagnosticar la template<class> void f() { return 1; } template<class> void f() { return 1; } . [temp.res]/8 con el nuevo cambio en negrita:

El programa está mal formado, no requiere diagnóstico, si:

  • no se puede generar una especialización válida para una plantilla o una subestación de una declaración constexpr if ([stmt.if]) dentro de una plantilla y la plantilla no se crea una instancia, o
  • [...]

No se puede generar una especialización válida para una plantilla que contenga static_assert cuya condición no sea dependiente y se evalúe como false , por lo que el programa está mal formado NDR.

static_assert s con una condición dependiente que puede evaluar como true para al menos un tipo no se ven afectadas.


Editar: mantengo esta auto-respuesta con ejemplos y explicaciones más detalladas de los malentendidos que conducen a estas preguntas. La respuesta breve de TC es estrictamente suficiente.

Después de releer la propuesta y de static_assert en el borrador actual , concluyo que mis preocupaciones estaban equivocadas. En primer lugar, el énfasis aquí debe estar en la definición de la plantilla.

mal formado; no se requiere diagnóstico para la definición de la plantilla

Si se static_assert una instancia de una plantilla, cualquier static_assert dispara como se esperaba. Esto presumiblemente juega bien con la declaración que cité:

... una declaración descartada no se crea una instancia.

Esto es un poco vago para mí, pero concluyo que significa que las plantillas que aparecen en la declaración descartada no se instanciarán. Sin embargo, otro código debe ser sintácticamente válido. Un static_assert(F) , [donde F es falso, ya sea literalmente o un valor constexpr] dentro de una cláusula if constexpr descartada seguirá "mordiendo" cuando se instancia la plantilla que contiene el static_assert . O (no se requiere, a merced del compilador) ya en la declaración si se sabe que siempre es falso.

Ejemplos: ( demostración en vivo )

#include <type_traits> template< typename T> constexpr void some_library_foo(){ static_assert(std::is_same<T,int>::value); } template< typename T> constexpr void other_library_bar(){ static_assert(std::is_same<T,float>::value); } template< typename T> constexpr void buzz(){ // This template is ill-formated, (invalid) no diagnostic required, // since there are no T which could make it valid. (As also mentioned // in the answer by T.C.). // That also means that neither of these are required to fire, but // clang does (and very likely all compilers for similar cases), at // least when buzz is instantiated. static_assert(! std::is_same<T,T>::value); static_assert(false); // does fire already at declaration // with latest version of clang } template<class T, bool IntCase> void g() { if constexpr (IntCase){ some_library_foo<T>(); // Both two static asserts will fire even though within if constexpr: static_assert(!IntCase) ; // ill-formated diagnostic required if // IntCase is true static_assert(IntCase) ; // ill-formated diagnostic required if // IntCase is false // However, don''t do this: static_assert(false) ; // ill-formated, no diagnostic required, // for the same reasons as with buzz(). } else { other_library_bar<T>(); } } int main(){ g<int,true>(); g<float,false>(); //g<int,false>(); // ill-formated, diagnostic required //g<float,true>(); // ill-formated, diagnostic required }

El texto estándar en static_assert es notablemente corto. En standardese, es una forma de hacer que el programa esté mal formado con diagnóstico (como @immibis también señaló):

7.6 ... Si el valor de la expresión cuando se convierte así es verdadero, la declaración no tiene efecto. De lo contrario, el programa está mal formado, y el mensaje de diagnóstico resultante (1.4) incluirá el texto del literal de cadena, si se proporciona uno ...