我是否误解了这个默认参数 shared_ptr 的范围?
Have I misunderstood the scope of this default argument shared_ptr?
看看这个:
#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()
returns 时被调用,而在我使用 GCC 4.8.5 的 64 位 CentOS 7 系统上,确实如此。
但是,在我的 32 位 CentOS 6 系统上,在 devtoolset-2 下使用 GCC 4.8.2(我 认为 在 gcc-linaro-arm-linux-gnueabihf-4.8-2013.10_linux
下,我的 Raspberry Pi 工具链),它不会。
查看代码,考虑到 C++11 在 4.8 中的实验性质,我觉得这像是一个编译器错误。但我也可能在某个地方陷入了 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)
析构函数应在 bar
return 时或调用 bar
的完整表达式结束时调用。
如果我们查看 [[=25=]]/4(C++17 草案)我们有
When a function is called, each parameter (11.3.5) shall be initialized (11.6, 15.8, 15.1) with its corresponding argument.[...]It is implementation-defined whether the lifetime of a parameter ends when the function in which it is defined returns or at the end of the enclosing full-expression.[...]
所以p
应该在函数开始时初始化为null FooPtr
,移动赋值给MakeFoo
的return,最后销毁(在在 bar
末尾或 bar
return 秒后 main
.
转调用删除器)
正如 Nathan 所展示的,我对指针生命周期的假设是 standard-correct。
删除器未被调用似乎是 GCC 或 libstdc++ 错误,可能 bug 60367 考虑到链接的评论解决了它,症状看起来很相似并且在 GCC 4.8.5 之前已修复.
用 = FooPtr{}
替换 = {}
似乎是一个可行的解决方法。
注意哦,还有a regression in 7.2 and some older "8.0" trunk builds that may cause bad behaviour in similar circumstances (thanks Arne Vogel!).
看看这个:
#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()
returns 时被调用,而在我使用 GCC 4.8.5 的 64 位 CentOS 7 系统上,确实如此。
但是,在我的 32 位 CentOS 6 系统上,在 devtoolset-2 下使用 GCC 4.8.2(我 认为 在 gcc-linaro-arm-linux-gnueabihf-4.8-2013.10_linux
下,我的 Raspberry Pi 工具链),它不会。
查看代码,考虑到 C++11 在 4.8 中的实验性质,我觉得这像是一个编译器错误。但我也可能在某个地方陷入了 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)
析构函数应在 bar
return 时或调用 bar
的完整表达式结束时调用。
如果我们查看 [[=25=]]/4(C++17 草案)我们有
When a function is called, each parameter (11.3.5) shall be initialized (11.6, 15.8, 15.1) with its corresponding argument.[...]It is implementation-defined whether the lifetime of a parameter ends when the function in which it is defined returns or at the end of the enclosing full-expression.[...]
所以p
应该在函数开始时初始化为null FooPtr
,移动赋值给MakeFoo
的return,最后销毁(在在 bar
末尾或 bar
return 秒后 main
.
正如 Nathan 所展示的,我对指针生命周期的假设是 standard-correct。
删除器未被调用似乎是 GCC 或 libstdc++ 错误,可能 bug 60367 考虑到链接的评论解决了它,症状看起来很相似并且在 GCC 4.8.5 之前已修复.
用 = FooPtr{}
替换 = {}
似乎是一个可行的解决方法。
注意哦,还有a regression in 7.2 and some older "8.0" trunk builds that may cause bad behaviour in similar circumstances (thanks Arne Vogel!).