¿Debería este código no compilar en C++17?
clang boost-variant (1)
Estaba actualizando un proyecto para usar C ++ 17 y encontré algunos casos en los que el código que seguía este patrón estaba causando un error de compilación en las versiones recientes de Clang:
#include <boost/variant.hpp>
struct vis : public boost::static_visitor<void>
{
void operator()(int) const { }
};
int main()
{
boost::variant<int> v = 0;
boost::apply_visitor(vis{}, v);
}
Usando clang v8.0 en modo C ++ 17, esto falla con el siguiente error :
<source>:11:30: error: temporary of type ''boost::static_visitor<void>'' has protected destructor
boost::apply_visitor(vis{}, v);
^
/opt/compiler-explorer/libs/boost_1_64_0/boost/variant/static_visitor.hpp:53:5: note: declared protected here
~static_visitor() = default;
Sin embargo,
compila limpiamente en modo C ++ 14
.
Descubrí que si cambio la inicialización de la abrazadera
vis{}
a paréntesis
vis()
, entonces se compila correctamente en ambos modos.
Cada versión de gcc que he probado permite ambas variantes en el modo C ++ 17.
¿Es este un cambio correcto en el comportamiento de C ++ 14 a C ++ 17, o se trata de un error de argot? Si es correcto, ¿por qué ahora no es válido en C ++ 17 (o quizás siempre lo fue, pero el argot solo lo permite en las revisiones estándar anteriores)?
clang es correcto aquí. Aquí hay un ejemplo reducido:
struct B {
protected:
B() { }
};
struct D : B { };
auto d = D{};
En C ++ 14,
D
no
es un agregado porque tiene una clase base, por lo que
D{}
es una inicialización "normal" (no agregada) que invoca al constructor predeterminado de
D
, que a su vez invoca al constructor predeterminado de
B
Esto está bien, porque
D
tiene acceso al constructor por defecto de
B
En C ++ 17, la definición de agregado se amplió: ahora se permiten las clases base (siempre que no sean
virtual
).
D
es ahora un agregado, lo que significa que
D{}
es la inicialización agregada.
Y en la inicialización agregada, esto significa que nosotros (el que llama) estamos inicializando todos los subobjetos, incluido el subobjeto de clase base.
Pero
no
tenemos acceso al constructor de
B
(está
protected
), por lo que no podemos invocarlo, por lo que está mal formado.
No temas, la solución es fácil. Usa paréntesis:
auto d = D();
Esto vuelve a invocar al constructor por defecto de
D
como antes.