已导出模板实例化的旧样式名称重整

更新:2007 年 11 月

Visual C++ 一致性工作的一个方面是允许重载函数模板和常规模板的专用化。

例如,下面的示例在 Visual Studio .NET 2003 中将可编译,但在 Visual Studio .NET 中将失败:

// bc_exported_templ_instan.cpp
template<typename T, typename U>
T ConvertTo(const U &u) {
   return static_cast<T>(u);
}

char ConvertTo(const int &i) {
   return static_cast<char>(i);
}

int main() {
   char c1 = ConvertTo(1);
   char c2 = ConvertTo<char, int>(2);
}

为了支持对函数模板和常规模板的专用化进行重载,编译器更改了它为函数模板的专用化创建修饰名的方式。更新的修饰名现在包括模板实参的编码,以及函数形参和返回类型的编码。对于前面示例中的函数,Visual Studio .NET 2003 中的 C++ 编译器将生成以下名称修饰:

?ConvertTo@@YADABH@Z      – char ConvertTo(const int &);
??$ConvertTo@DH@@YADABH@Z – char ConvertTo<char, int>(const int &);

这不应意味着应用程序开发方式的任何更改。此更改的确会对用户产生影响的一个方面是:是否会导出尚未用新编译器重新编译的应用程序中所使用 DLL 中的函数模板的专用化。例如:

// bc_exported_templ_instan2.h
#include <iostream>
#ifdef _DLL_EXPORT
#define DLL_LINKAGE __declspec(dllexport)
#else
#define DLL_LINKAGE __declspec(dllimport)
#endif

template<typename T>
void f(const T &rT) {
   std::cout << "i = " << rT << std::endl;
}
template DLL_LINKAGE void f<int>(const int &);

将编译下面的代码来创建 DLL:

// bc_exported_templ_instan2.cpp
// compile with: /D_DLL_EXPORT /EHsc /LD
#include "bc_exported_templ_instan2.h"

如果随后执行:

dumpbin /exports bc_exported_templ_instan2.dll

您将看到函数模板已导出专用化的修饰名为 ??$f@H@@YAXABH@Z。

如果现有应用程序依赖于此 DLL,并且此应用程序是用 Visual Studio .NET(或更早版本)中的 Visual C++ 编译器生成的,而它们无法或不想重新生成此应用程序,则会出现运行时错误,因为应用程序将需要具有旧名称的入口点。但是,新 DLL 将只会用新名称导出函数。

// bc_exported_templ_instan3.cpp
// compile with: /EHsc /link bc_exported_templ_instan2.lib
#include "bc_exported_templ_instan2.h"
int main() {
   f(1);
}

修复此问题方法是:更改 DLL 的生成方式,以使其为函数模板的专用化同时导出旧名称和新名称。通过为链接程序使用 /export 可实现此目的。例如:

cl /D_DLL_EXPORT /EHsc /LD a.cpp /link /export:?f@@YAXABH@Z=??$f@H@@YAXABH@Z 

这将指示链接为两个指定的名称都创建导出,但让它们映射到同一符号。

请参见

参考

Visual C++ 编译器中的重大更改