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()
{
}
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()
{
}
Esto parece que podría estar relacionado con este compromiso en noviembre. https://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20161121/177858.html
Debe ser un error de compilación. ¿Se ha informado?
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.
- Su
int x{0}
cae claramente en la categoría miembro de datos no estáticos con inicializador (C ++ 11) . - El
declval
no requiere un tipo completo, ese es el punto de ello, como se explica en esta bonita exposición .
¿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.