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

当我在不同的对象文件中使用专门的模板时,链接时会出现“多重定义”错误。我找到的唯一解决方案涉及使用“ inline”函数,但它看起来只是一些变通方法。不使用“ 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”注释,代码就会编译并运行,但是这对我来说似乎只是某种“解决办法”: 如果专门化的函数很大并且使用了很多次怎么办?我会得到一个大的二进制吗?还有别的办法吗?如果有,怎么做?如果没有,为什么?

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

谢谢

56181 次浏览

关键字 inline更多的是告诉编译器符号将出现在多个对象文件中而不违反“一个定义规则”,而不是告诉编译器实际的内联,编译器可以决定是否这样做。

The problem you are seeing is that without the inline, the function will be compiled in all translation units that include the header, violating the ODR. Adding inline there is the right way to go. Otherwise, you can forward declare the specialization and provide it in a single translation unit, as you would do with any other function.

您已经在头部显式实例化了一个模板(void Hello<T>::print_hello(T var))。这将创建多个定义。你可以用两种方法来解决:

1)内联实例化。

2)在头部声明实例化,然后在 cpp 中实现它。

直观地说,当您完全专门化某些内容时,它不再依赖于模板参数——因此,除非您将专门化内联,否则需要将其放在。Cpp 文件,而不是。否则你就会像大卫说的那样,违反单一定义规则。注意,当您部分专门化模板时,部分专门化仍然依赖于一个或多个模板参数,因此它们仍然存在于。H 档案。

下面是与这个问题相关的一些 C + + 11标准的 piece:

函数模板的显式专门化只有在 使用内联说明符声明或定义为已删除,并且 独立于它的函数模板是否内联:

模板 void f (T){/* ... < em >/}模板 inline T G (T){/ ... */}

Template < > inline void f < > (int){/* ... < em >/}//OK: inline Template < > int g < > (int){/ ... */}//OK: not inline ー 最后一个例子]

因此,如果在 *.h文件中对模板进行一些明确的(也就是完整的)专门化,那么仍然需要 inline来帮助您摆脱对 ODR的违反。