【问题标题】:LNK2019 (VS 2008) with full implementation of template function using template function pointersLNK2019 (VS 2008) 使用模板函数指针完整实现模板函数
【发布时间】:2014-06-26 15:10:50
【问题描述】:

以下最小代码可以在 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);
}

但使用 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)".

显然整个函数实现都在那里,但编译器似乎抛弃了foo 函数的实现。如果注释行被激活,则链接器会找到该符号。

我认为(因为 g++ 编译得很好)这是有效代码,所以我想 VS 2008 中存在一些错误,或者我在这里做错了什么?有谁知道这个的解决方法/解决方案?最终代码必须与 Visual Studio 2008 一起使用,并且在实际代码中不可能猜测所有模板类型组合(即,我无法显式实例化所有可用类型的函数:这​​里只是 T,在实际代码中,最多使用任意类的5个模板参数)。

【问题讨论】:

  • 这里有两个小例子:1 和 2。它们都在带有-Wall -Wextra -pedantic-errors 标志的gcc 4.8.2 和clang 3.4 中成功编译(在c++03 和c++11 标准中)。
  • VS2008 是一个非常古老的编译器,考虑到 C++ 目前的发展速度。尝试升级到最新最好的。
  • @Constructor 感谢您的检查!这意味着我的代码原则上是正确的。
  • @TemplateRex 我有其他版本的 VS,但通常是客户决定他们有什么理由必须使用哪个编译器。
  • 此代码在 Visual Studio 2008(版本 9.0.30729.1 SP)中运行并打印“5”

标签: c++ templates visual-studio-2008 function-pointers lnk2019


【解决方案1】:

原问题

回答原始问题;这是一个错误,有解决方法吗?

是的,您似乎在 VS2008 中发现了一个错误,我已经使用 VS2008 和 VS2013.2 进行了测试,但出现了相同的链接器错误。我鼓励您向Microsoft 提交错误报告。有没有变通办法,我相信可能有。

正如您所指出的,编译器似乎在衰减到 void (*Function)(T,void*) 和链接时需要它之间的某个位置“松散”了模板 foo&lt;int&gt; 的隐式实例化。玩了一下代码,我想可能涉及到apply(M)模板和微软的模板解析技术;因为,如果apply 只是将int 作为其参数apply(int)(即没有模板),它似乎很乐意编译和链接它。

要解决此问题,可以按如下方式更改代码(添加默认构造函数并将apply 调用更改为从kernel 的实例进行)。我知道这可能看起来很难看;但它可以解决问题,并且可以帮助您解决项目中的问题。

#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 {
  void (*dummy)(T,void*);
public:
  kernel() : dummy(Function) {
    // "Force" an implicit instantiation...
    // dummy can be either a member variable or declared in
    // in the constructor here. It exists only to "force"
    // the implicit instantiation.
    // Alternative...
    //void* dummy = (void*)Function;
    //(void)dummy; // silence "unused" warnings
  }

  // Function dispatcher
  template<class M>
  inline static void apply(M t) {
    Function(t,0);
  }
};

int main()
{
  kernel<int,foo>().apply(5);
  // The kernel temporary instantiation is only needed once for the
  // following line to link as well.
  //kernel<int,foo>::apply(5);
}

代码编译链接VS2008、VS2013和gcc。


代码如何与现代编译器一起工作?

参考原问题上发布的cmets;为什么或如何使用现代编译器?它以两个 C++ 工具为中心。

  1. 函数指针衰减
    • 如有任何附加规则(例如模板)
  2. 隐式函数模板实例化

当提供foo 作为void(*Function)(T,void*) 的参数时,会发生衰减并使用指针,就好像使用了&amp;foo。

函数到指针的转换4.3

1 函数类型 T 的左值可以转换为“指向 T 的指针”类型的纯右值。结果是指向函数的指针

函数到指针的转换参考第 13.4 节,了解可能重载函数时的附加规则。注意&amp; 的使用细节以及函数是模板的情况(强调我的)。

重载函数13.4的地址

1 函数模板名被认为是命名一组重载函数...重载函数名前面可以加&运算符。

2 如果名称是函数模板,模板实参推导完成(14.8.2.2),如果实参推导成功,则使用生成的模板实参列表生成单个函数模板特化,它被添加到所考虑的重载函数集中。

给定指针和编译器对函数 T 所需类型的推导 foo 在这种情况下为 int。然后编译器生成函数void foo(int,void*)的代码,然后在链接期间使用。

隐式实例化 14.7.1

3 除非函数模板特化已被显式实例化或显式特化,否则当在需要函数定义存在的上下文中引用特化时,函数模板特化将被隐式实例化。

引自C++ WD n3797

【讨论】:

  • 你不需要投到void *,我有更好的解决方案(见我的编辑)。
  • @Niall 非常感谢您的详细回答。我会尝试将这个想法融入到概念中。
  • foo 是一个函数模板,不是一个简单的函数。为什么这里适用函数到指针的转换?
  • @Constructor,没错,它不是一个简单的函数。在实例化之前不存在任何代码。一旦实例化,函数的代码就存在,就像一个简单的函数一样。给定使用foo 的上下文(需要一个指针),编译器能够推导出模板参数,实例化函数,然后应用转换并继续编译代码。引用 en.cppreference.com/w/cpp/language/function_template 在隐式实例化下提供了此 void (*ptr)(std::string) = f; // instantiates f&lt;string&gt;(string) 的精确示例。
  • @Constructor,还有一些值得一提的。 IIRC,隐式函数到指针的转换是旧的 C 兼容性。许多人会争辩说,更明确的形式 &amp;foo 应该受到青睐,但这两种形式仍然适用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多