当一个问题出现了新的表达形式new(std::nothrow) C;
哪里C
是一个类名,其构造函数抛出.使用以下代码查看下面的代码和实例g++
:
#includevoid* operator new(std::size_t size, const std::nothrow_t&) noexcept { void* p; p = malloc(size); std::cout << "operator new(std::nothrow)" << '\n'; return p; } void operator delete(void* p, const std::nothrow_t&) noexcept { free(p); std::cout << "operator delete(std::nothrow)" << '\n'; std::cout << p << '\n'; } class T{}; class C { int i; public: C(int i) : i{i} { std::cout << "C()" << '\n'; throw T{}; } ~C() { std::cout << "~C()" << '\n'; } }; int main() { C* c; try { c = new(std::nothrow) C(3); } catch (T&) { std::cout << "exception thrown in C(int) was caught" << '\n'; std::cout << c << '\n'; } }
g++
打印以下内容似乎是正确的:
operator new(std::nothrow) C() operator delete(std::nothrow) 0x13f9c20 exception thrown in C(int) was caught 0
然而,如果您使用clang
,您将获得以下输出:
operator new(std::nothrow) C() exception thrown in C(int) was caught 0x7fffecdeed00
也就是说,它好像clang
是不调用operator delete(void*, std::nothrow_t&)
在程序中定义,并呼吁相反,运营商在标准库.
奇怪的是,通过只删除表达式std::cout << p << '\n';
的operator delete(void*, std::nothrow_t&)
,在代码中定义的,clangs
似乎正确执行,打印:
operator new(std::nothrow) C() operator delete(std::nothrow) exception thrown in C(int) was caught 0x7fffc0ffc000
编辑
为了回应@TC下面的评论和其他人说上面的代码有未定义的行为,我在下面提供另一个代码,显示编译器应该如何操作,正确编译上面的代码片段,使用由以下代码提供的伪代码: @TC 在这里.另请参见此实例.要注意的重要一点是,这个代码不使用的新的表达 new(nothrow)
.
#includevoid * operator new(std::size_t n) { void* p; try { p = malloc(n); } catch (std::bad_alloc&) { throw; } std::cout << "operator new" << '\n'; return p; } void operator delete(void *p) noexcept { free(p); std::cout << "operator delete" << '\n'; } void* operator new(std::size_t size, const std::nothrow_t&) noexcept { void* p = malloc(size); std::cout << "operator new(std::nothrow)" << '\n'; return p; } void operator delete(void* p, const std::nothrow_t&) noexcept { free(p); std::cout << "operator delete(std::nothrow)" << '\n'; std::cout << p << '\n'; } class T {}; class C { int i; public: C(int i) : i{ i } { std::cout << "C()" << '\n'; throw T{}; } ~C() { std::cout << "~C()" << '\n'; } }; int main() { C *c; try { c = (C*)operator new(sizeof(C), std::nothrow); struct cleanup { void* p; bool active; ~cleanup() { if (active) operator delete(p, std::nothrow); } void dismiss() { active = false; } } guard = { (void*)c, true }; new(c) C{1}; guard.dismiss(); } catch ( std::bad_alloc& ) { c = nullptr; } catch (T&) { std::cout << "exception thrown in C() was caught" << '\n'; std::cout << c << '\n'; } }
g++
为此代码打印以下内容:
operator new(std::nothrow) C() operator delete(std::nothrow) 0x10c3c20 exception thrown in C() was caught 0x10c3c20
令人惊讶的是,clang
似乎使用此代码正确操作,该代码不使用new-expression new(nothrow)
,这清楚地表明clang
在编译此new-expression时存在错误.
在我的系统OS X 10.11.1上,std :: lib-provided operator delete
位于/usr/lib/libc++abi.dylib中.在类Unix系统上,这个签名通过赋予它"弱连接"而可以替换.当链接器看到两个相同的签名,并且其中一个具有弱连接时,它将更喜欢那个没有的签名.
我可以确认在我的系统上,operator delete(void*, std::nothrow_t const&)
与以下命令的链接很弱:
$ nm -gm /usr/lib/libc++abi.dylib |c++filt |grep nothrow_t 0000000000024406 (__TEXT,__text) weak external operator delete[](void*, std::nothrow_t const&) 00000000000243fc (__TEXT,__text) weak external operator delete(void*, std::nothrow_t const&) 00000000000243c0 (__TEXT,__text) weak external operator new[](unsigned long, std::nothrow_t const&) 000000000002437e (__TEXT,__text) weak external operator new(unsigned long, std::nothrow_t const&)
您能对系统进行类似的分析并报告结果吗?
更新
感谢TC下面关于如何复制症状的说明,它现在在我看来这是一个clang编译器代码生成错误,在3.7中引入,仍然存在于主干中,并且只能在-O2(不是-O1)重现或更低而不是-O3).
我认为错误报告是有序的,它应该有关于如何重现错误的良好指示(除非你希望它们给它一个低优先级).
PS
并设置,C *c = nullptr;
以便他们不浪费时间追逐不相关的UB.
第二次更新
我仍然无法使用clang tip-of-trunk在本地重现这一点.但我可以在以下网站上看到它:
http://melpon.org/wandbox/permlink/5zIRyPJpq32LfU0t
我还没有解释这种差异.也许我的行李箱比他们的更近?也许他们没有使用libc ++ abi?