【问题标题】:Unused template functions in shared library共享库中未使用的模板函数
【发布时间】:2010-08-05 17:16:20
【问题描述】:

我在一个用 C++ 编写的共享库中有一个模板函数(该函数在库中的任何地方都没有调用,所以它不应该生成,我错了吗?)[g++,Linux]

我尝试在应用程序中使用此模板函数,但编译器给出链接错误。我使用 objdump 搜索了该函数,但在 .so 中看不到该函数 有没有办法解决这个问题?

【问题讨论】:

标签: c++ templates


【解决方案1】:

模板函数属于库头文件,它们不在 DLL 共享库中编译。因此,将所有模板函数移动到一个标头中,并将该标头包含在您的应用程序中。

【讨论】:

  • 对不起,我忘记指定我在 Linux 中使用 g++。
  • @xyzt:这对 Linux 也有效。
  • 这是一种方法。模板本身不在共享库中,但这些模板的任何实例都在共享库中。有两种类型的实例化。 1) 隐式 2) 显式在链接共享库时都可以使用。
  • 如果我这样做,我似乎会收到关于多个符号定义的各种错误
【解决方案2】:

最简单的方法是将所有模板代码移动到头文件中(如其他答案中所述)
但是这不是唯一的解决方案。

模板不是实际的函数。

当有函数的实例化时,模板就变成了函数。这可以隐式或显式完成。任何时候使用该函数都会有一个隐式实例化。但您也可以显式实例化模板函数。

因此,您应该能够链接到共享库中模板函数的任何实例化版本。

头文件,所以我们遵守一个定义规则。

// tt.h
template<typename T>
int doStuff(Tconst &t);

源文件:

// tt.cpp
#include "tt.h"
template<typename T>
int doStuff(Tconst &t)
{
    return 4;
}

void plop()
{
    int x = 6;
    // implicit instanciation of doStuff<int>(int const&);
    doStuff(x);
}

// explicit instanciation of template
template int doStuff<float>(float const&);

如果我将以上内容编译成共享库。
然后会有两个可用的 doStuff() 方法。

  • doStuff(int const&) // 隐式实例化
  • doStuff(float const&) // 显式实例化

g++ -shared -o tt.so tt.cpp

现在,如果我们有一个单独的文件链接到共享库:

// main.cpp
#include "tt.h"

int main()
{
    doStuff(5);   // doStuff<int>()
    doStuff(6.0f); // doStuff<float>()
}

g++ main.cpp t.so

编译正常,即使 main 看不到任何模板代码。

【讨论】:

  • 此解决方案有效,但会强制您为计划使用的每种类型进行实例化。如果您正在编写库,这可能是个问题。
  • @Dacav:不完全是。这会强制您实例化要从共享库中导出的所有版本。这是一个优势,因为您可以控制可用的实例。如果您希望它们全部可用,则将其放在头文件中(如上面第 1 行所述)。
【解决方案3】:

编译器开发人员很难实现模板,因此,大多数 C++ 编译器实际上要求您将模板代码放在头文件中,甚至是完整的类实现(但有一些例外和技巧可以避免这种情况)。

对于您的情况,您只需将函数模板移动到头文件(可能与其他函数模板一起)并在需要时导入。

希望对您有所帮助。

【讨论】:

    【解决方案4】:

    您要使用的函数需要在某个地方实例化,无论是在您的库中还是在您的代码中。您的编译器应该在您的程序代码中为您生成实例化,除非原型被声明为 extern。听起来您想在库中显式实例化该函数。在您库中的一个源文件中,您只需添加如下一行:

    模板 return_type 函数名(param_type,param_type);

    并且应该显式生成函数的代码。

    更多关于显式实例化: http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/explicit_instantiation.htm

    【讨论】:

      【解决方案5】:

      前段时间我发现自己处于同样的情况,所以我对此进行了调查。基本上模板不会产生任何目标代码,它们只是编译器的定义。当您实例化像 std::list&lt;Foo&gt; l 这样的对象时,编译器将为 Foo 对象列表生成实例和专用代码。

      这会导致两个后果:

      • 不可能将模板类代码写入专用的 .cpp 文件(就像对普通模块所做的那样),因为您事先不知道需要实例化哪些数据类型;
      • 两个都使用 std::list&lt;Foo&gt; 作为数据类型的 .cpp 文件将生成相同可执行代码的副本。

      第二点使链接阶段的事情变得困难,因为链接器应该处理双重定义。一种可能的链接策略是为每个目标代码保留一份副本。更智能的链接器可能会尝试优化事物,但这会带来很大的复杂性。

      我想这就是为什么 gcc 编译 C++ 需要这么长时间,而使用 C 编译速度要快得多。

      【讨论】:

      • 第一个结果是假的,第二个可能是。并且所有现代链接器都可以毫无问题地管理(可能的)多个实例。
      • @Neil Butterworth:我编辑了我的第一点以使其更加清晰。
      • 还是错了。您是在谈论 C++ 缺少模板的“导出”功能吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-07
      • 2011-02-28
      • 2011-08-04
      • 1970-01-01
      相关资源
      最近更新 更多