假设我有两个具有相同参数类型和名称的函数(不在同一个程序中):
std::string foo(int x) {
return "hello";
}
int foo(int x) {
return x;
}
Run Code Online (Sandbox Code Playgroud)
一旦编译,它们会具有相同的损坏名称吗?
是 C++ 中重整名称的返回类型部分吗?
由于重整计划没有标准化,所以这个问题没有唯一的答案。最接近实际答案的是查看由最常见的重整方案生成的重整名称。据我所知,这些是 GCC 和 MSVC 方案,按字母顺序排列,所以......
为了测试这一点,我们可以使用一个简单的程序。
#include <string>
#include <cstdlib>
std::string foo(int x) { return "hello"; }
//int foo(int x) { return x; }
int main() {
// Assuming executable file named "a.out".
system("nm a.out");
}
Run Code Online (Sandbox Code Playgroud)
用 GCC 或 Clang 编译和运行,它会列出它包含的符号。根据未注释的函数,结果将是:
// GCC:
// ----
std::string foo(int x) { return "hello"; } // _Z3fooB5cxx11i
// foo[abi:cxx11](int)
int foo(int x) { return x; } // _Z3fooi
// foo(int)
// Clang:
// ------
std::string foo(int x) { return "hello"; } // _Z3fooi
// foo(int)
int foo(int x) { return x; } // _Z3fooi
// foo(int)
Run Code Online (Sandbox Code Playgroud)
GCC 方案包含的信息相对较少,不包括返回类型:
_Z用于“功能”。3foo为::foo。i对于int.尽管如此,它们在使用 GCC(而不是使用 Clang)编译时是不同的,因为 GCC 表明该std::string版本使用cxx11ABI。
请注意,它仍然会跟踪返回类型,并确保签名匹配;它只是不使用函数的损坏名称来执行此操作。
为了测试这一点,我们可以使用一个简单的程序,如上。
#include <string>
#include <cstdlib>
std::string foo(int x) { return "hello"; }
//int foo(int x) { return x; }
int main() {
// Assuming object file named "a.obj".
// Pipe to file, because there are a lot of symbols when <string> is included.
system("dumpbin/symbols a.obj > a.txt");
}
Run Code Online (Sandbox Code Playgroud)
使用 Visual Studio 编译并运行,并a.txt会列出它包含的符号。根据未注释的函数,结果将是:
std::string foo(int x) { return "hello"; }
// ?foo@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z
// class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl foo(int)
int foo(int x) { return x; }
// ?foo@@YAHH@Z
// int __cdecl foo(int)
Run Code Online (Sandbox Code Playgroud)
MSVC 方案包含整个声明,包括未明确指定的内容:
foo@for ::foo,后跟@to 终止。@。Y用于“非成员函数”。Afor __cdecl.H为int。?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@(后@接终止)为std::basic_string<char, std::char_traits<char>, std::allocator<char>>(std::string简称)。Hfor int(后跟@终止)。Zfor throw(...); 除非它是别的东西,否则这个从被破坏的名称中被省略,可能是因为 MSVC 无论如何都会忽略它。如果每个编译单元的声明都不相同,这允许它向您抱怨。
通常,大多数编译器在分别针对 *nix 或 Windows 时会使用其中一种方案(或有时是其变体),但这并不能保证。例如...
其他编译器使用的方案要归功于Agner Fog 的 PDF。
检查生成的符号,很明显 GCC 的修改方案没有提供与 MSVC 相同级别的针对 Machiavelli 的保护。考虑以下:
// foo.cpp
#include <string>
// Simple wrapper class, to avoid encoding `cxx11 ABI` into the GCC name.
class MyString {
std::string data;
public:
MyString(const char* const d) : data(d) {}
operator std::string() { return data; }
};
// Evil.
MyString foo(int i) { return "hello"; }
// -----
// main.cpp
#include <iostream>
// Evil.
int foo(int);
int main() {
std::cout << foo(3) << '\n';
}
Run Code Online (Sandbox Code Playgroud)
如果我们分别编译每个源文件,然后尝试将目标文件链接在一起......
MyString由于不是cxx11ABI 的一部分,导致MyString foo(int)被损坏为_Z3fooi,就像int foo(int). 这允许链接目标文件,并生成可执行文件。尝试运行它会导致段错误。?foo@@YAHH@Z;正如我们提供的那样?foo@@YA?AVMyString@@H@Z,链接将失败。考虑到这一点,包含返回类型的重整方案更安全,即使不能仅根据返回类型的差异重载函数。
| 归档时间: |
|
| 查看次数: |
1464 次 |
| 最近记录: |