c++ - 我相信这是 clang 中的一个错误,与构造函数抛出的放置新表达式有关

标签 c++ clang new-operator c++14

new-expression 的形式为 new(std::nothrow) C; 时,问题就出现了其中 C是其构造函数抛出的类名。请参阅下面的代码和 live example使用 g++ :

#include <iostream>

void* 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

编辑

回应@T.C. 的评论。下面以及其他那些说上面的代码具有未定义行为的人,我在下面展示了另一个代码,它显示了编译器应该如何操作,以正确编译上面的代码片段,使用 @T.C. 提供的伪代码。 here .另见 live example .需要注意的重要一点是,这段代码不使用 new-expression new(nothrow) .

#include <iostream>

void * 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(nothrow) ,这清楚地表明 clang编译此新表达式时出现错误。

最佳答案

在我的系统 OS X 10.11.1 上,std::lib 提供的 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&)

你能在你的系统上做类似的分析并报告结果吗?

更新

感谢 T.C. 下面关于如何复制症状的说明,现在在我看来这是一个 clang 编译器代码生成错误,在 3.7 中引入,仍然存在于 tip-of-trunk 中,并且只能在 - O2(不是 -O1 或更低但不是 -O3)。

我认为错误报告是有序的,它应该有关于如何重现错误的很好的说明(除非你希望他们给予这个低优先级)。

附言

并设置 C *c = nullptr; 这样他们就不会浪费时间追逐不相关的 UB。

第二次更新

我仍然无法使用 clang tip-of-trunk 在本地重现此内容。但我可以在以下网站上看到它:

http://melpon.org/wandbox/permlink/5zIRyPJpq32LfU0t

我还没有对这种差异的解释。也许我的树干尖比他们的新?也许他们没有使用 libc++abi?

关于c++ - 我相信这是 clang 中的一个错误,与构造函数抛出的放置新表达式有关,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34120659/

相关文章:

C++ 启动失败。找不到二进制文件

c++ - clang sanitizer 会检查未初始化的内存吗?

c# - 获取隐式对象创建表达式的类型

c++ - 如果使用 'new' 来实例化对象而不将其分配给变量,会发生什么情况?

c++ - clang 4.0.0 cuda 和 cmake 最小警告的最佳实践

c++ - 全局替换所有新运营商

c++ - 让编译器最终选择使用哪种类型

c++ - for循环c++中的“冒号”和 'auto'?需要一些帮助来理解语法

c++ - 我可以创建对空可选值的引用吗?

c++ - Clang 如何编译模板实例化?