c++ clang c++17 boost-variant

¿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.