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

时间:2022-08-25 17:04:47

When I use a specialized template in different object files, I get a "multiple definition" error when linking. The only solution I found involves using the "inline" function, but it just seems like some workaround. How do I solve that without using the "inline" keyword? If that's not possible, why?

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

Here is the example code:

这是示例代码:

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

Finally:

最后:

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

If I uncomment the "inline" inside hello.h, the code will compile and run, but that just seems like some kind of "workaround" to me: what if the specialized function is big and used many times? Will I get a big binary? Is there any other way to do this? If yes, how? If not, why?

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

I tried to look for answers, but all I got was "use inline" without any further explanation.

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

Thanks

谢谢

3 个解决方案

#1


88  

Intuitively, when you fully specialize something, it doesn't depend on a template parameter any more -- so unless you make the specialization inline, you need to put it in a .cpp file instead of a .h or you end up violating the one definition rule as David says. Note that when you partially specialize templates, the partial specializations do still depend on one or more template parameters, so they still go in a .h file.

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

#2


36  

The keyword inline is more about telling the compiler that the symbol will be present in more than one object file without violating the One Definition Rule than about actual inlining, which the compiler can decide to do or not to do.

关键字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.

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

#3


16  

You've explicitly instantiated a template in your header (void Hello<T>::print_hello(T var)). This will create multiple definitions. You can solve it in two ways:

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

1) Make your instantiation inline.

1)使您的实例化内联。

2) Declare the instantiation in a header and then implement it in a cpp.

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

#1


88  

Intuitively, when you fully specialize something, it doesn't depend on a template parameter any more -- so unless you make the specialization inline, you need to put it in a .cpp file instead of a .h or you end up violating the one definition rule as David says. Note that when you partially specialize templates, the partial specializations do still depend on one or more template parameters, so they still go in a .h file.

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

#2


36  

The keyword inline is more about telling the compiler that the symbol will be present in more than one object file without violating the One Definition Rule than about actual inlining, which the compiler can decide to do or not to do.

关键字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.

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

#3


16  

You've explicitly instantiated a template in your header (void Hello<T>::print_hello(T var)). This will create multiple definitions. You can solve it in two ways:

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

1) Make your instantiation inline.

1)使您的实例化内联。

2) Declare the instantiation in a header and then implement it in a cpp.

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