JPA*_*M69 8 c++ templates function-pointers lnk2019 visual-studio-2008
以下最小代码在GNU C++中编译和链接很好:
#include <iostream>
// Simple function
template<class T>
void foo(T a,void* = 0) {
std::cout << a << std::endl;
}
// A distpatching class
template<
class T,
void (*Function)(T,void*)
>
class kernel {
public:
// Function dispatcher
template<class M>
inline static void apply(M t) {
Function(t,0);
}
};
int main()
{
kernel<int,foo>::apply(5);
//foo(5,0);
}
Run Code Online (Sandbox Code Playgroud)
但是使用Visual Studio 2008会产生错误
error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""void __cdecl foo<int>(int,void *)" (??$foo@H@@YAXHPAX@Z)" in Funktion ""public: static void __cdecl kernel<int,&void __cdecl foo<int>(int,void *)>::apply<int>(int)" (??$apply@H@?$kernel@H$1??$foo@H@@YAXHPAX@Z@@SAXH@Z)".
Run Code Online (Sandbox Code Playgroud)
显然整个函数实现都存在,但似乎编译器抛弃了foo函数的实现.如果激活注释行,则链接器将找到该符号.
我认为(因为g ++编译得很好)这是有效的代码,所以我想VS 2008中有一些错误,或者我在这里做错了什么?有谁知道解决方法/解决方案吗?最终的代码必须与Visual Studio 2008一起使用,并且在实际代码中不可能猜出所有模板类型组合(即我无法显式实例化所有可用类型的函数:这里只是T,在实际代码中,直到使用具有任意类的5个模板参数).
回答原来的问题;这是一个错误吗,有解决方法吗?
\n\n是的,看起来您在 VS2008 中发现了一个错误,我已经用 VS2008 和 VS2013.2 对其进行了测试,具有相同的链接器错误。我鼓励您向Microsoft提交错误报告提交错误报告。有没有解决办法,我相信可能有。
\n\n正如您所指出的,编译器看起来像是在衰减到链接时需要模板foo<int>之间的某个位置“丢失”了模板的隐式实例化。void (*Function)(T,void*)稍微玩了一下代码,我认为这可能涉及到apply(M)模板和微软的模板解析技术;因为,如果apply仅将 anint作为其参数apply(int)(即没有模板),它似乎很乐意编译和链接它。
要解决此问题,可以按如下方式更改代码(添加默认构造函数并更改apply从 的实例进行的调用kernel)。我知道这可能看起来很难看;但它可以解决该问题,并且可以帮助您解决项目中的问题。
#include <iostream>\n\n// Simple function\ntemplate<class T>\nvoid foo(T a,void* = 0) {\n std::cout << a << std::endl;\n}\n\n// A distpatching class\ntemplate<class T,\n void(*Function)(T,void*)>\nclass kernel {\n void (*dummy)(T,void*);\npublic:\n kernel() : dummy(Function) {\n // "Force" an implicit instantiation...\n // dummy can be either a member variable or declared in\n // in the constructor here. It exists only to "force"\n // the implicit instantiation.\n // Alternative...\n //void* dummy = (void*)Function;\n //(void)dummy; // silence "unused" warnings\n }\n\n // Function dispatcher\n template<class M>\n inline static void apply(M t) {\n Function(t,0);\n }\n};\n\nint main()\n{\n kernel<int,foo>().apply(5);\n // The kernel temporary instantiation is only needed once for the\n // following line to link as well.\n //kernel<int,foo>::apply(5);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n该代码使用VS2008、VS2013和gcc进行编译和链接。
\n\n参考对原始问题的评论;为什么或者如何在现代编译器中使用它?它以两个 C++ 设施为中心。
\n\n当foo作为 的参数提供时void(*Function)(T,void*),会发生衰减并使用指针,就像&foo使用过一样。
\n\n\n函数到指针的转换 4.3
\n\n1 函数类型 T 的左值可以转换为指向 T 的指针 \xe2\x80\x9c 类型的纯右值。\xe2\x80\x9d 结果是指向函数的指针
\n
当存在可能的重载函数时,函数到指针的转换请参考第 13.4 节以了解其他规则。请注意有关使用的详细信息&以及该函数是模板的情况(强调我的)。
\n\n\n13.4 重载函数地址
\n\n1 函数模板名称被认为是命名一组重载函数...重载函数名称前面可以加上 & 运算符。
\n\n2 如果名称是函数模板,则完成模板参数推导(14.8.2.2),如果参数推导成功,则使用生成的模板参数列表生成单个函数模板特化,将其添加到重载集合中考虑的功能。
\n
给定指针和编译器对本例中T函数foo所需类型的推导。int然后编译器生成该函数的代码void foo(int,void*),然后在链接期间使用它。
\n\n\n\n隐式实例化 14.7.1
\n\n3 除非函数模板特化已被显式实例化或显式特化,否则当在需要函数定义存在的上下文中引用该特化时,该函数模板特化将被隐式实例化。
\n