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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4445654/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 15:26:06  来源:igfitidea点击:

multiple definition of template specialization when using different objects

c++templates

提问by pzanoni

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 中的“内联”,代码将编译并运行,但这对我来说似乎是某种“解决方法”:如果专用函数很大并且被多次使用怎么办?我会得到一个大的二进制文件吗?有没有其他方法可以做到这一点?如果是,如何?如果不是,为什么?

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

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

Thanks

谢谢

回答by Stuart Golodetz

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 文件中。

回答by David Rodríguez - dribeas

The keyword inlineis 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 inlinethere 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。添加inline那里是正确的方法。否则,您可以转发声明专业化并在单个翻译单元中提供它,就像您对任何其他函数所做的一样。

回答by Edward Strange

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<T>::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 中实现它。

回答by Francis

Here is some pieceof C++11 standard related to this issue:

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

An explicit specialization of a function template is inline only if it is declared with the inline specifier or defined as deleted, and independently of whether its function template is inline. [ Example:

template void f(T) { /* ... / } template inline T g(T) { /... */ }

template<> inline void f<>(int) { /* ... / } // OK: inline template<> int g<>(int) { /... */ } // OK: not inline — end example ]

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

template void f(T) { /* ... / } 模板内联 T g(T) { /... */ }

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

So if you make some explicit(aka full) specializations of templates in *.hfile, then you will still need inlineto help you get rid of the violation of ODR.

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