c++ c++11 language-lawyer

c++ - “El inicializador de miembro predeterminado es necesario dentro de la definición de clase adjunta fuera de las funciones de miembro”: ¿mi código está mal formado?



c++11 language-lawyer (5)

#include <utility> struct foo { int x{0}; foo() noexcept = default; void f() noexcept(noexcept(std::declval<foo&>())) {} }; int main() { }

ejemplo vivo en Godbolt

El código anterior compila con cualquier versión de g ++ que probé, y con clang ++ de 3.6 a 3.9.1, pero no compila con clang ++ 4.0.0 :

test.cpp:6:5: error: default member initializer for ''x'' needed within definition of enclosing class ''foo'' outside of member functions foo() noexcept = default; ^ type_traits:126:26: note: in instantiation of template class ''std::is_function<foo &>'' requested here : public conditional<_B1::value, _B1, __or_<_B2, _B3, _Bn...>>::type ^ type_traits:154:39: note: in instantiation of template class ''std::__or_<std::is_function<foo &>, std::is_reference<foo &>, std::is_void<foo &> >'' requested here : public integral_constant<bool, !_Pp::value> ^ type_traits:598:14: note: in instantiation of template class ''std::__not_<std::__or_<std::is_function<foo &>, std::is_reference<foo &>, std::is_void<foo &> > >'' requested here : public __not_<__or_<is_function<_Tp>, is_reference<_Tp>, ^ type_traits:121:26: note: in instantiation of template class ''std::is_object<foo &>'' requested here : public conditional<_B1::value, _B1, _B2>::type ^ type_traits:635:14: note: in instantiation of template class ''std::__or_<std::is_object<foo &>, std::is_reference<foo &> >'' requested here : public __or_<is_object<_Tp>, is_reference<_Tp>>::type ^ type_traits:1667:33: note: in instantiation of template class ''std::__is_referenceable<foo &>'' requested here template<typename _Tp, bool = __is_referenceable<_Tp>::value> ^ type_traits:1678:14: note: in instantiation of default argument for ''__add_rvalue_reference_helper<foo &>'' required here : public __add_rvalue_reference_helper<_Tp> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ type_traits:2267:12: note: in instantiation of template class ''std::add_rvalue_reference<foo &>'' requested here inline typename add_rvalue_reference<_Tp>::type ^ wtfff.cpp:7:32: note: while substituting explicitly-specified template arguments into function template ''declval'' void f() noexcept(noexcept(std::declval<foo&>())) {} ^ wtfff.cpp:5:9: note: default member initializer declared here int x{0}; ^

¿Mi código está mal formado? Si es así, ¿cuál es el significado del error?

Tenga en cuenta que eliminar el noexcept del constructor o el inicializador {0} de x hará que el código se compile.


Como estados estándar de C ++ 11

§ 5.3.7

El operador noexcept determina si la evaluación de su operando, que es un operando no evaluado (Cláusula 5), ​​puede lanzar una excepción (15.1).

noexcept-expression: noexcept ( expression )

El resultado del operador noexcept es una constante de tipo bool y es un valor de r. El resultado del operador noexcept es falso si en un contexto potencialmente evaluado la expresión contendría

- una llamada potencialmente evaluada a una función , una función miembro, un puntero a una función o un puntero a una función miembro que no tiene una especificación de excepción no lanzable (15.4), a menos que la llamada sea una expresión constante (5.19),

- una expresión de lanzamiento potencialmente evaluada (15.1),

- una expresión Dynamic_cast potencialmente evaluada dynamic_cast (v), donde T es un tipo de referencia, que requiere una verificación en tiempo de ejecución (5.2.7), o

- una expresión tipográfica potencialmente evaluada (5.2.8) aplicada a una expresión de glvalue cuyo tipo es un tipo de clase polimórfica (10.3).

De lo contrario, el resultado es verdadero.

y

template <class T> typename add_rvalue_reference<T>::type declval() noexcept; // as unevaluated operand

El add_rvalue_reference es de tipo add_rvalue_reference de transformación y, no se dice explícitamente, pero no requiere que la definición de objeto / función sea instanciada.

A partir de aquí queda claro que struct foo; ... noexcept(std::declval<foo>()) struct foo; ... noexcept(std::declval<foo>()) es un código legal. Por noexcept , donde parte noexcept es equivalente a noexcept(true) que es equivalente a solo noexcept , y noexcept(noexcept no tiene sentido, para obtener un constructor noexcept specifier tiene que hacer esto noexcept(foo()) . Este último también es válido, pero, desafortunadamente, los compiladores no pueden lidiar con él, probablemente debido al orden en que construyen la unidad para el código que no es C ++ 11, y aún no han transformado este modelo. Esto refleja la naturaleza. del error que se encuentra en la implementación de libc ++ en particular. Por alguna razón, add_rvalue_reference debido a la presencia del especificador noexcept intenta usar la declaración del constructor y, dado que eso sucede fuera de la función miembro, como se mencionó anteriormente, falla. Este es un error de la biblioteca.


Esta es la manera sintáctica correcta que puedo decir.

#include <utility> struct foo { int x{0}; foo() noexcept {} // = default; void f() noexcept(noexcept(std::declval<foo&>())) {} }; int main() { }



Su código está bien por lo que puedo decir. Clang parece tener problemas con el constructor = default lugar de simplemente definir un constructor predeterminado manualmente. Tiene el siguiente spiel en su código fuente al respecto:

DR1351: Si el inicializador de refuerzo o igual de un miembro de datos no estáticos invoca a un constructor predeterminado predeterminado de su clase o de una clase adjunta en una subexpresión potencialmente evaluada, el programa está mal formado.

Esta resolución es impracticable: la especificación de excepción del constructor predeterminado puede ser necesaria en un contexto no evaluado, en particular, en el operando de una expresión noexcept, y no podemos calcular una especificación de excepción para una clase encerrada.

Cualquier intento de resolver la especificación de excepción de un constructor predeterminado por defecto antes de que el inicializador se complete de manera léxica, finalmente llegará a este punto en el que podemos diagnosticarlo.

Creo que puede ser incorrecto recoger el error, personalmente. Pero menciona específicamente "constructor predeterminado por defecto".

Lo siguiente parece funcionar:

#include <utility> struct foo { int x{0}; foo() noexcept {} // = default; void f() noexcept(noexcept(std::declval<foo&>())) {} }; int main() { }


Su uso está bien.

¿Lo que queda? Error del compilador, supongo. ¿Qué tal esto como evidencia? Use un tipo z completo en lugar de foo en su declval .

#include <utility> struct z{}; struct foo { int x{0}; foo() noexcept = default; void f() noexcept( noexcept( std::declval<z>() ) ) {} }; int main() { }

Clang 4.0.0 en godbolt todavía falla de la misma manera. Desafortunadamente, no tengo el clang 4.0.0 disponible en una máquina para probar, por lo que no puedo decir si es Clang o godbolt para cierto.