¿He entendido mal el scope de este argumento predeterminado shared_ptr?

Mira esto:

#include  #include  using Foo = int; using FooPtr = std::shared_ptr; FooPtr makeFoo() { FooPtr f{ new Foo(), [](Foo* ptr) { delete ptr; std::cerr << "!\n"; } }; return f; } void bar(FooPtr p = {}) { p = makeFoo(); } int main() { bar(); } // Expected output: '!' // Failure case: no output (deleter not invoked?) 

Esperaba que se shared_ptr el shared_ptr shared_ptr cuando vuelva bar() , y en mi sistema CentOS 7 de 64 bits que usa GCC 4.8.5, lo hace.

Sin embargo, en mi sistema CentOS 6 de 32 bits que utiliza GCC 4.8.2 en devtoolset-2 (también creo que en gcc-linaro-arm-linux-gnueabihf-4.8-2013.10_linux , mi cadena de herramientas Raspberry Pi), no es así.

Mirando el código, y dada la naturaleza experimental de C ++ 11 en 4.8, esto me huele a error de comstackción. Pero también podría estar cayendo en una trampa de UB en algún lugar (o simplemente malinterpretando cómo debería funcionar esto).

¿Quién tiene la culpa? ¿Y cómo debo arreglarlo?


Trabaja en

 Using built-in specs. COLLECT_GCC=g++ COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper Target: x86_64-redhat-linux Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux Thread model: posix gcc version 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC) 

Falla en

 Using built-in specs. COLLECT_GCC=g++ COLLECT_LTO_WRAPPER=/opt/rh/devtoolset-2/root/usr/libexec/gcc/i686-redhat-linux/4.8.2/lto-wrapper Target: i686-redhat-linux Configured with: ../configure --prefix=/opt/rh/devtoolset-2/root/usr --mandir=/opt/rh/devtoolset-2/root/usr/share/man --infodir=/opt/rh/devtoolset-2/root/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --enable-languages=c,c++,fortran,lto --enable-plugin --with-linker-hash-style=gnu --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.2-20140120/obj-i686-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.2-20140120/obj-i686-redhat-linux/cloog-install --with-mpc=/builddir/build/BUILD/gcc-4.8.2-20140120/obj-i686-redhat-linux/mpc-install --with-tune=generic --with-arch=i686 --build=i686-redhat-linux Thread model: posix gcc version 4.8.2 20140120 (Red Hat 4.8.2-15) (GCC) 

Se debe llamar al destructor cuando regresa la bar o al final de la expresión completa en la que se llama la bar .

Si observamos [expr.call] / 4 (borrador de C ++ 17) tenemos

Cuando se llama a una función, cada parámetro (11.3.5) se inicializará (11.6, 15.8, 15.1) con su argumento correspondiente. […] Se define por implementación si la vida útil de un parámetro finaliza cuando la función en la que Se define como retornos o al final de la expresión completa adjunta. […]

Por lo tanto, se debe inicializar p a un FooPtr nulo al comienzo de la función, mover el retorno asignado de MakeFoo y finalmente destruirlo (a su vez llamar al eliminador) al final de la bar o después de que la bar regrese en main .

Como ha demostrado Nathan, mis suposiciones sobre la vida útil del puntero eran correctas en cuanto a los estándares.

El hecho de que el deleter no se haya invocado parece ser un error de GCC o libstdc ++, posiblemente el error 60367 dado que el comentario vinculado lo resuelve, los síntomas parecen similares y se corrigió antes de GCC 4.8.5.

Reemplazar = {} con = FooPtr{} parece ser una solución viable.


Tenga en cuenta que también hay una regresión en 7.2 y algunas versiones anteriores de “8.0” que pueden causar un mal comportamiento en circunstancias similares (gracias Arne Vogel !).

    Intereting Posts