我是否误解了此默认参数shared_ptr的范围?

时间:2018-03-26 18:17:35

标签: c++ gcc shared-ptr libstdc++ default-arguments

看看这个:

#include <iostream>
#include <memory>

using Foo = int;
using FooPtr = std::shared_ptr<Foo>;

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?)

我希望在shared_ptr返回时调用bar()删除器,在使用GCC 4.8.5的64位CentOS 7系统上调用它。

然而,在我的32位CentOS 6系统上使用devtoolset-2下的GCC 4.8.2(我认为<{em>在gcc-linaro-arm-linux-gnueabihf-4.8-2013.10_linux下,我的Raspberry Pi工具链),它没有

查看代码,并给出了4.8中的C ++ 11的实验性质,这对我来说就像编译错误。但我也可能陷入某个地方的UB陷阱(或者只是误解了这些东西应该如何运作)。

谁有错?我该如何解决?

适用于

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)

失败
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)

2 个答案:

答案 0 :(得分:5)

应该在bar返回时调用析构函数,或者在调用bar的完整表达式结束时调用析构函数。

如果我们查看[expr.call] / 4(C ++ 17草案),我们有

  

当调用一个函数时,每个参数(11.3.5)都应该用相应的参数进行初始化(11.6,15.8,15.1)。[...]实现定义参数的生命周期是否结束定义它的函数返回或在封闭的完整表达式的末尾。[...]

所以p应该在函数开始时初始化为null FooPtr,移动分配MakeFoo的返回值,然后最终销毁(反过来调用删除者)at在bar返回barmain之后。

答案 1 :(得分:4)

正如内森所表明的那样,我对指针生命周期的假设是标准正确的。

没有调用删除程序似乎确实是GCC或libstdc ++错误,可能是bug 60367,因为链接的注释解决了它,症状看似相似,并且在GCC 4.8.5之前修复了。 / p>

= {}替换= FooPtr{}似乎是一种可行的解决方法。

请注意,在类似情况下,还有a regression in 7.2 and some older "8.0" trunk builds可能会导致不良行为(感谢Arne Vogel!)。