在C++中是否可以指定使用哪个删除操作符?

Pet*_*pme 7 c++ clang++ c++14

我正在检测一些代码,并注意到有了C++ 14的功能,有两个新的delete运算符(来自http://en.cppreference.com/w/cpp/memory/new/operator_delete):

这些是5-6)如果提供了用户定义的替换,则调用而不是(1-2),除了它的实现定义在删除不完整类型和数组的对象时是否调用(1-2)或(5-6)非类和易破坏的类类型(自C++ 17以来).标准库实现与(1-2)相同.

我已经超载了这些并且想要专门调用这两个.当我用gcc重载这两个时,我没有问题.使用clang ++我得到一个未定义的引用operator delete(void*)

这是代码

void* operator new(long unsigned int howMuch) {
    return reinterpret_cast<void*>(0xdeadbeef);
}

void* operator new[](long unsigned int howMuch) {
    return reinterpret_cast<void*>(0xdeadbeef);
}

void operator delete(void* what, long unsigned int howmuch) {
        if(what != reinterpret_cast<void*>(0xdeadbeef)) __builtin_trap();
        if(howmuch != 1) __builtin_trap();
}

extern "C"
void _start() {
    delete new char;
    asm("syscall" : : "a"(60) : ); 
}
Run Code Online (Sandbox Code Playgroud)

用gcc编译:g++ -ggdb -std=c++14 -nostdlib -fno-builtin -fno-exceptions 1.cc没有问题,运行正常.

用llvm/clang可以做到这一点吗?

Iwa*_*amp 1

您可以显式调用大小或非大小删除运算符,如下所示:

\n\n
char* ptr = new char;\ndelete ptr; // compiler selects which to call\noperator delete(ptr); // explicitly call the non-sized delete \noperator delete(ptr, 1); // explicitly call sized delete\n
Run Code Online (Sandbox Code Playgroud)\n\n

完整的例子:

\n\n
void* operator new(long unsigned int howMuch) {                                                                                                                                               \n    return reinterpret_cast<void*>(0xdeadbeef);\n}\n\nvoid* operator new[](long unsigned int howMuch) {\n    return reinterpret_cast<void*>(0xdeadbeef);\n}\n\nvoid operator delete(void* what) {\n    if(what != reinterpret_cast<void*>(0xdeadbeef)) __builtin_trap();\n}\n\nvoid operator delete(void* what, long unsigned int howmuch) {\n    if(what != reinterpret_cast<void*>(0xdeadbeef)) __builtin_trap();\n    if(howmuch != 1) __builtin_trap();\n}\n\nextern "C"\nvoid _start() {\n    char* ptr = new char;\n    delete ptr;\n    operator delete(ptr);\n    operator delete(ptr, 1);\n    asm("syscall" : : "a"(60) : ); \n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

编译并查看生成的代码,可以清楚地知道在以下情况下调用哪些运算符:

\n\n
$ clang++ -std=c++14 -nostdlib  -fno-builtin  -fno-exceptions -fsized-deallocation sized-deallocation.cpp -o sized-deallocation.bin && gdb -batch -ex \'file sized-deallocation.bin\' -ex \'disassemble _start\' | c++filt\nDump of assembler code for function _start:\n   0x0000000000401070 <+0>: push   %rbp\n   0x0000000000401071 <+1>: mov    %rsp,%rbp\n   0x0000000000401074 <+4>: sub    $0x10,%rsp\n   0x0000000000401078 <+8>: mov    $0x1,%eax\n   0x000000000040107d <+13>:    mov    %eax,%edi\n   0x000000000040107f <+15>:    callq  0x401000 <operator new(unsigned long)>\n   0x0000000000401084 <+20>:    mov    %rax,-0x8(%rbp)\n   0x0000000000401088 <+24>:    mov    -0x8(%rbp),%rax\n   0x000000000040108c <+28>:    cmp    $0x0,%rax\n   0x0000000000401090 <+32>:    mov    %rax,-0x10(%rbp)\n   0x0000000000401094 <+36>:    je     0x4010aa <_start+58>\n   0x000000000040109a <+42>:    mov    $0x1,%eax\n   0x000000000040109f <+47>:    mov    %eax,%esi\n   0x00000000004010a1 <+49>:    mov    -0x10(%rbp),%rdi\n   0x00000000004010a5 <+53>:    callq  0x401040 <operator delete(void*, unsigned long)>\n   0x00000000004010aa <+58>:    mov    -0x8(%rbp),%rdi\n   0x00000000004010ae <+62>:    callq  0x401020 <operator delete(void*)>\n   0x00000000004010b3 <+67>:    mov    $0x1,%eax\n   0x00000000004010b8 <+72>:    mov    %eax,%esi\n   0x00000000004010ba <+74>:    mov    -0x8(%rbp),%rdi\n   0x00000000004010be <+78>:    callq  0x401040 <operator delete(void*, unsigned long)>\n   0x00000000004010c3 <+83>:    mov    $0x3c,%eax\n   0x00000000004010c8 <+88>:    syscall \n   0x00000000004010ca <+90>:    add    $0x10,%rsp\n   0x00000000004010ce <+94>:    pop    %rbp\n   0x00000000004010cf <+95>:    retq   \nEnd of assembler dump.\n
Run Code Online (Sandbox Code Playgroud)\n\n

您使用 Clang 的实际原因undefined reference to `operator delete(void*)\'是(正如@TC所说)Clang 需要该-fsized-deallocation标志来启用C++14 大小的释放

\n\n

如果使用以下命令,您的示例编译不会出现错误:

\n\n
$ clang++ -std=c++14 -nostdlib  -fno-builtin  -fno-exceptions -fsized-deallocation sized-deallocation.cpp -o sized-deallocation.bin && gdb -batch -ex \'file sized-deallocation.bin\' -ex \'disassemble _start\' | c++filt\nDump of assembler code for function _start:\n   0x0000000000401070 <+0>: push   %rbp\n   0x0000000000401071 <+1>: mov    %rsp,%rbp\n   0x0000000000401074 <+4>: sub    $0x10,%rsp\n   0x0000000000401078 <+8>: mov    $0x1,%eax\n   0x000000000040107d <+13>:    mov    %eax,%edi\n   0x000000000040107f <+15>:    callq  0x401000 <operator new(unsigned long)>\n   0x0000000000401084 <+20>:    mov    %rax,-0x8(%rbp)\n   0x0000000000401088 <+24>:    mov    -0x8(%rbp),%rax\n   0x000000000040108c <+28>:    cmp    $0x0,%rax\n   0x0000000000401090 <+32>:    mov    %rax,-0x10(%rbp)\n   0x0000000000401094 <+36>:    je     0x4010aa <_start+58>\n   0x000000000040109a <+42>:    mov    $0x1,%eax\n   0x000000000040109f <+47>:    mov    %eax,%esi\n   0x00000000004010a1 <+49>:    mov    -0x10(%rbp),%rdi\n   0x00000000004010a5 <+53>:    callq  0x401040 <operator delete(void*, unsigned long)>\n   0x00000000004010aa <+58>:    mov    -0x8(%rbp),%rdi\n   0x00000000004010ae <+62>:    callq  0x401020 <operator delete(void*)>\n   0x00000000004010b3 <+67>:    mov    $0x1,%eax\n   0x00000000004010b8 <+72>:    mov    %eax,%esi\n   0x00000000004010ba <+74>:    mov    -0x8(%rbp),%rdi\n   0x00000000004010be <+78>:    callq  0x401040 <operator delete(void*, unsigned long)>\n   0x00000000004010c3 <+83>:    mov    $0x3c,%eax\n   0x00000000004010c8 <+88>:    syscall \n   0x00000000004010ca <+90>:    add    $0x10,%rsp\n   0x00000000004010ce <+94>:    pop    %rbp\n   0x00000000004010cf <+95>:    retq   \nEnd of assembler dump.\n
Run Code Online (Sandbox Code Playgroud)\n\n

由于 Clang 3.7 C++14 大小的释放默认被禁用:

\n\n

Clang 中的 C++ 支持 > C++14 实现状态 > C++ 大小的释放 N3778

\n\n
\n

(7):在 Clang 3.7 及更高版本中,仅当用户传递标志时才启用大小释放-fsized-deallocation。用户必须通过显式提供或使用 C++ 标准库来提供大小释放函数的定义。libstdc++在5.0版本中添加了这些功能,libc++在3.7版本中添加了这些功能。

\n
\n\n

Clang 3.7 发行说明 > Clang 3.7 中有哪些新功能?> 新的编译器标志

\n\n
\n

C++14 的大小释放功能现在由标志控制-fsized-deallocation。此功能依赖于尚未广泛部署的库支持,因此用户必须提供额外的标志才能获得额外的功能。

\n
\n\n

进行此更改的原因是当时广泛部署的标准库中缺少这些函数(2015-03-19):

\n\n

C++14:由于 ABI 损坏,默认禁用大小释放

\n\n
\n

没有广泛部署的标准库提供大小释放函数,因此我们必须询问用户是否希望我们使用大小释放函数。将来,当部署此类库时,我们可以教驱动程序检测它们并启用此功能。

\n
\n\n

可以在此处找到此选项的手动输入:

\n\n

Clang 命令行参数参考 > 编译标志 > 与目标无关的编译选项

\n\n
\n

-fsized-deallocation,-fno-sized-deallocation

\n\n

启用 C++14 大小的全局释放函数

\n
\n