and c++ c++11 constexpr noexcept constant-expression

c++ - and - La función `static constexpr` llamada en una expresión constante es... ¿un error?



constexpr in c++ (1)

Tengo el siguiente código:

class MyClass { static constexpr bool foo() { return true; } void bar() noexcept(foo()) { } };

Yo esperaría que dado que foo() es una función static constexpr , y como se define antes de bar se declara la bar , esto sería perfectamente aceptable.

Sin embargo, g++ me da el siguiente error:

error: ‘static constexpr bool MyClass::foo()’ called in a constant expression

Esto es ... menos que útil, ya que la capacidad de llamar a una función en una expresión constante es el punto entero de constexpr .

clang++ es un poco más útil. Además de un mensaje de error que indica que el argumento de noexcept debe ser una expresión constante, dice:

note: undefined function ''foo'' cannot be used in a constant expression note: declared here static constexpr bool foo() { return true; } ^

Entonces ... ¿este es un problema de compilación de dos pases? ¿El problema es que el compilador está intentando declarar todas las funciones miembro en la clase antes de que se defina cualquiera de ellas? (Tenga en cuenta que fuera del contexto de una clase, ninguno de los compiladores arroja un error). Esto me sorprende; intuitivamente, no veo ninguna razón para que las funciones miembro static constexpr no puedan utilizarse en cualquiera y todas las expresiones constantes, dentro o fuera de la clase.


Como TC demostró con algunos enlaces en un comentario, el estándar no es muy claro al respecto; un problema similar surge con los tipos de retorno finales usando decltype(memberfunction()) .

El problema central es que los miembros de la clase generalmente no se consideran declarados hasta después de que la clase en la que se declara esté completa. Por lo tanto, independientemente del hecho de que foo sea static constexpr y su declaración preceda a la de bar , no puede considerarse "disponible" para su uso en una expresión constante hasta que se complete MyClass .

Como lo señaló Shafik Yaghmour , hay un intento dentro de la norma para evitar una dependencia en el orden de los miembros dentro de una clase, y obviamente permitir que el ejemplo en la pregunta original compile introduciría una dependencia de ordenamiento (ya que foo tendría que ser declarado ante el bar ). Sin embargo, ya existe una dependencia menor en el ordenamiento, porque aunque constexpr funciones constexpr no se pueden llamar dentro de noexcept , una expresión noexcept sí misma podría depender de una declaración anterior dentro de la clase:

class MyClass { // void bar() noexcept(noexcept(foo())); // ERROR if declared here static constexpr bool foo(); void bar() noexcept(noexcept(foo())); // NO ERROR }

(Tenga en cuenta que esto no es realmente una violación de 3.3.7, ya que todavía hay un solo programa correcto que es posible aquí).

Este comportamiento puede ser realmente una violación de la norma; TC señala (en un comentario a continuación) que foo aquí debería consultarse en el ámbito de toda la clase. Tanto g ++ 4.9.2 como clang ++ 3.5.1 fallan con un error cuando la bar se declara primero, pero se compila sin errores ni advertencias cuando se declara foo primero. EDIT: clang ++ trunk-revision 238946 (desde poco antes del lanzamiento de 3.7.0) no falla cuando la bar se declara primero; g ++ 5.1 todavía falla.

Curiosamente, la siguiente variación causa un "especificador de excepción diferente" con clang ++ pero no con g ++:

class MyClass { static constexpr bool foo2(); void bar2() noexcept(noexcept(foo2())); }; constexpr bool MyClass::foo2() { return true; } void MyClass::bar2() noexcept(noexcept(MyClass::foo2())) { }

De acuerdo con el error, la especificación de noexcept para la declaración de bar2 evalúa como noexcept(false) , que luego se considera una falta de coincidencia para noexcept(noexcept(MyClasss::foo2())) .