使用不同对象时模板特化的多重定义

时间:2010-12-15 00:27:25

标签: c++ templates

当我在不同的目标文件中使用专用模板时,链接时出现“多重定义”错误。我找到的唯一解决方案涉及使用“内联”功能,但它似乎只是一些解决方法。如何在不使用“inline”关键字的情况下解决这个问题?如果那不可能,为什么?

以下是示例代码:

paulo@aeris:~/teste/cpp/redef$ cat hello.h 
#ifndef TEMPLATE_H
#define TEMPLATE_H

#include <iostream>

template <class T>
class Hello
{
public:
    void print_hello(T var);
};

template <class T>
void Hello<T>::print_hello(T var)
{
    std::cout << "Hello generic function " << var << "\n";
}

template <> //inline
void Hello<int>::print_hello(int var)
{
    std::cout << "Hello specialized function " << var << "\n";
}

#endif

paulo@aeris:~/teste/cpp/redef$ cat other.h 
#include <iostream>

void other_func();

paulo@aeris:~/teste/cpp/redef$ cat other.c 
#include "other.h"

#include "hello.h"

void other_func()
{
    Hello<char> hc;
    Hello<int> hi;

    hc.print_hello('a');
    hi.print_hello(1);
}

paulo@aeris:~/teste/cpp/redef$ cat main.c 
#include "hello.h"

#include "other.h"

int main()
{
    Hello<char> hc;
    Hello<int> hi;

    hc.print_hello('a');
    hi.print_hello(1);

    other_func();

    return 0;
}

paulo@aeris:~/teste/cpp/redef$ cat Makefile
all:
    g++ -c other.c -o other.o -Wall -Wextra
    g++ main.c other.o -o main -Wall -Wextra

最后:

paulo@aeris:~/teste/cpp/redef$ make
g++ -c other.c -o other.o -Wall -Wextra
g++ main.c other.o -o main -Wall -Wextra
other.o: In function `Hello<int>::print_hello(int)':
other.c:(.text+0x0): multiple definition of `Hello<int>::print_hello(int)'
/tmp/cc0dZS9l.o:main.c:(.text+0x0): first defined here
collect2: ld returned 1 exit status
make: ** [all] Erro 1

如果我取消注释hello.h中的“inline”,代码将编译并运行,但这对我来说似乎是某种“解决方法”:如果专用函数很大并且多次使用该怎么办?我会得到一个大二进制文件吗?有没有其他方法可以做到这一点?如果有,怎么样?如果没有,为什么?

我试图寻找答案,但我得到的只是“使用内联”而没有任何进一步的解释。

由于

4 个答案:

答案 0 :(得分:100)

直观地说,当你完全专门化某些东西时,它不再依赖于模板参数 - 所以除非你使内联专业化,你需要把它放在.cpp文件而不是.h或者你结束大卫说,违反了一个定义规则。请注意,当您对模板进行部分特化时,部分特化仍然依赖于一个或多个模板参数,因此它们仍然位于.h文件中。

答案 1 :(得分:41)

关键字inline更多的是告诉编译器符号将出现在多个目标文件中而不违反单一定义规则而不是实际内联,编译器可以决定做或不做

您遇到的问题是,如果没有内联,该函数将在包含标题的所有翻译单元中编译,违反ODR。添加inline是正确的方法。否则,您可以转发声明专业化并将其提供在单个翻译单元中,就像使用任何其他功能一样。

答案 2 :(得分:19)

您已在标头(void Hello<T>::print_hello(T var))中明确实例化了模板。这将创建多个定义。您可以通过两种方式解决它:

1)使你的实例化内联。

2)在标头中声明实例化,然后在cpp。

中实现它

答案 3 :(得分:0)

以下是与此问题相关的C ++ 11标准的piece

  

仅当函数模板的显式特化才是内联的   用内联说明符声明或定义为删除,并且   与其功能模板是否内联无关。 [示例:

     

模板无效f(T){/ * ... /}模板内联T   g(T){/ ... * /}

     

template <>内联void f <>(int){/ * ... /} // OK:内联   template <> int g <>(int){/ ... * /} // OK:不内联—   结束示例]

因此,如果您在*.h文件中对模板进行了一些显式的(又称完全的)专业化,那么您仍然需要inline来摆脱对 ODR 的违反>。