¿Es posible is_constexpr en C++ 11?

¿Es posible is_constexpr en C++ 11?

Una vez lo escribí (EDITAR:ver más abajo las limitaciones y explicaciones). Desde https://stackoverflow.com/a/10287598/34509:

template<typename T> 
constexpr typename remove_reference<T>::type makeprval(T && t) {
  return t;
}

#define isprvalconstexpr(e) noexcept(makeprval(e))

Sin embargo, hay muchos tipos de expresiones constantes. La respuesta anterior detecta expresiones constantes de prvalue.

Explicación

El noexcept(e) expresión da false si y si e contiene

  • una llamada potencialmente evaluada a una función que no tiene una especificación de excepción de no lanzamiento a menos que la llamada sea una expresión constante,
  • un throw potencialmente evaluado expresión,
  • una forma arrojable potencialmente evaluada de dynamic_cast o typeid .

Tenga en cuenta que la plantilla de función makeprval no se declara noexcept , por lo que la llamada debe ser una expresión constante para que la primera viñeta no se aplique, y esto es de lo que abusamos. Necesitamos que las otras viñetas no se apliquen también, pero afortunadamente, ambas son throw y un dynamic_cast arrojable o typeid tampoco están permitidos en expresiones constantes, así que está bien.

Limitaciones

Desafortunadamente, existe una limitación sutil, que puede o no ser importante para usted. La noción de "potencialmente evaluado" es mucho más conservadora que los límites de lo que se aplican a las expresiones constantes. Así que el anterior noexcept puede dar falsos negativos. Informará que algunas expresiones no son expresiones constantes de valor, aunque lo sean. Ejemplo:

constexpr int a = (0 ? throw "fooled!" : 42);
constexpr bool atest = isprvalconstexpr((0 ? throw "fooled!" : 42));

En el anterior atest es falso, aunque la inicialización de a logrado Eso es porque para ser una expresión constante, basta con que las subexpresiones no constantes "malvadas" "nunca se evalúen", aunque esas subexpresiones malvadas son potencialmente evaluado, formalmente.


A partir de 2017, is_constexpr no es posible en C++11. Eso suena extraño, así que déjame explicarte un poco de la historia.

Primero, agregamos esta característica para resolver un defecto:http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1129

Johannes Schaub - litb publicó una macro de detección de constexpr que se basó en la disposición de que las expresiones constantes son implícitamente noexcept. Esto funcionó en C++ 11, pero al menos algunos compiladores nunca lo implementaron (por ejemplo, clang). Luego, como parte de C++17, evaluamos la eliminación de especificaciones de excepción en desuso de C++17. Como efecto secundario de esa redacción, eliminamos accidentalmente esa disposición. Cuando el Grupo de Trabajo Central discutió la posibilidad de volver a agregar la disposición, se dieron cuenta de que había algunos problemas serios al hacerlo. Puede ver los detalles completos en el informe de errores de LLVM. Entonces, en lugar de agregarlo nuevamente, decidimos considerarlo un defecto contra todas las versiones del estándar y lo eliminamos retroactivamente.

El efecto de esto es que, que yo sepa, no hay manera de detectar si una expresión se puede usar como una expresión constante.


Sí, esto es posible. Una forma de hacerlo (que es válida incluso con el reciente noexcept cambios) es aprovechar las reglas de conversión de restricción de C++11:

(énfasis mío). La inicialización de la lista generalmente no permite reducir las conversiones y, cuando se combina con SFINAE, podemos crear gadgets para detectar si una expresión arbitraria es una expresión constante:

// p() here could be anything
template<int (*p)()> std::true_type is_constexpr_impl(decltype(int{(p(), 0U)}));
template<int (*p)()> std::false_type is_constexpr_impl(...);
template<int (*p)()> using is_constexpr = decltype(is_constexpr_impl<p>(0));

constexpr int f() { return 0; }
int g() { return 0; }
static_assert(is_constexpr<f>());
static_assert(!is_constexpr<g>());

Demostración en vivo.

La clave aquí es que int{(expr, 0U)} contiene una conversión de restricción de unsigned int a int (y por lo tanto está mal formado), a menos que expr es una expresión constante, en cuyo caso la expresión completa (expr, 0U) es una expresión constante cuyo valor evaluado se ajusta al tipo int .