C++ “未定义引用”模板类构造函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8752837/
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 19:03:45  来源:igfitidea点击:

"Undefined reference to" template class constructor

c++templatescompiler-errorscodeblocks

提问by Heathcliff

I have no idea why this is happenning, since I think I have everything properly declared and defined.

我不知道为什么会发生这种情况,因为我认为我已经正确声明和定义了所有内容。

I have the following program, designed with templates. It's a simple implementation of a queue, with the member functions "add", "substract" and "print".

我有以下程序,用模板设计。这是一个队列的简单实现,具有成员函数“add”、“substract”和“print”。

I have defined the node for the queue in the fine "nodo_colaypila.h":

我在“nodo_colaypila.h”中定义了队列的节点:

#ifndef NODO_COLAYPILA_H
#define NODO_COLAYPILA_H

#include <iostream>

template <class T> class cola;

template <class T> class nodo_colaypila
{
        T elem;
        nodo_colaypila<T>* sig;
        friend class cola<T>;
    public:
        nodo_colaypila(T, nodo_colaypila<T>*);

};

Then the implementation in "nodo_colaypila.cpp"

然后在“nodo_colaypila.cpp”中实现

#include "nodo_colaypila.h"
#include <iostream>

template <class T> nodo_colaypila<T>::nodo_colaypila(T a, nodo_colaypila<T>* siguiente = NULL)
{
    elem = a;
    sig = siguiente;//ctor
}

Afterwards, the definition and declaration of the queue template class and its functions:

然后是队列模板类及其功能的定义和声明:

"cola.h":

“可乐.h”:

#ifndef COLA_H
#define COLA_H

#include "nodo_colaypila.h"

template <class T> class cola
{
        nodo_colaypila<T>* ult, pri;
    public:
        cola<T>();
        void anade(T&);
        T saca();
        void print() const;
        virtual ~cola();

};


#endif // COLA_H

"cola.cpp":

“可乐.cpp”:

#include "cola.h"
#include "nodo_colaypila.h"

#include <iostream>

using namespace std;

template <class T> cola<T>::cola()
{
    pri = NULL;
    ult = NULL;//ctor
}

template <class T> void cola<T>::anade(T& valor)
{
    nodo_colaypila <T> * nuevo;

    if (ult)
    {
        nuevo = new nodo_colaypila<T> (valor);
        ult->sig = nuevo;
        ult = nuevo;
    }
    if (!pri)
    {
        pri = nuevo;
    }
}

template <class T> T cola<T>::saca()
{
    nodo_colaypila <T> * aux;
    T valor;

    aux = pri;
    if (!aux)
    {
        return 0;
    }
    pri = aux->sig;
    valor = aux->elem;
    delete aux;
    if(!pri)
    {
        ult = NULL;
    }
    return valor;
}

template <class T> cola<T>::~cola()
{
    while(pri)
    {
        saca();
    }//dtor
}

template <class T> void cola<T>::print() const
{
    nodo_colaypila <T> * aux;
    aux = pri;
    while(aux)
    {
        cout << aux->elem << endl;
        aux = aux->sig;
    }
}

Then, I have a program to test these functions as follows:

然后,我有一个程序来测试这些功能,如下所示:

"main.cpp"

“主.cpp”

#include <iostream>
#include "cola.h"
#include "nodo_colaypila.h"

using namespace std;

int main()
{
    float a, b, c;
    string d, e, f;
    cola<float> flo;
    cola<string> str;

    a = 3.14;
    b = 2.71;
    c = 6.02;
    flo.anade(a);
    flo.anade(b);
    flo.anade(c);
    flo.print();
    cout << endl;

    d = "John";
    e = "Mark";
    f = "Matthew";
    str.anade(d);
    str.anade(e);
    str.anade(f);
    cout << endl;

    c = flo.saca();
    cout << "First In First Out Float: " << c << endl;
    cout << endl;

    f = str.saca();
    cout << "First In First Out String: " << f << endl;
    cout << endl;

    flo.print();
    cout << endl;
    str.print();

    cout << "Hello world!" << endl;
    return 0;
}

But when I build, the compiler throws errors in every instance of the template class:

但是当我构建时,编译器会在模板类的每个实例中抛出错误:

undefined reference to `cola(float)::cola()'...(it's actually cola'<'float'>'::cola(), but this doesn't let me use it like that.)

对 `cola(float)::cola()' 的未定义引用...(它实际上是可乐'<'float'>'::cola(),但这不允许我那样使用它。)

And so on. Altogether, 17 warnings, counting the ones for the member functions being called in the program.

等等。总共 17 个警告,包括程序中调用的成员函数的警告。

Why is this? Those functions and constructors WERE defined. I thought that the compiler could replace the "T" in the template with "float", "string" or whatever; that was the advantage of using templates.

为什么是这样?这些函数和构造函数是定义好的。我认为编译器可以用“float”、“string”或其他任何东西替换模板中的“T”;这就是使用模板的优势。

I read somewhere here that I should put the declaration of each function in the header file for some reason. Is that right? And if so, why?

我在这里读到,出于某种原因,我应该将每个函数的声明放在头文件中。那正确吗?如果是这样,为什么?

Thanks in advance.

提前致谢。

回答by Aaron McDaid

This is a common question in C++ programming. There are two valid answers to this. There are advantages and disadvantages to both answers and your choice will depend on context. The common answer is to put all the implementation in the header file, but there's another approach will will be suitable in some cases. The choice is yours.

这是 C++ 编程中的常见问题。对此有两个有效的答案。两种答案各有利弊,您的选择将取决于上下文。常见的答案是将所有实现放在头文件中,但在某些情况下还有另一种方法适用。这是你的选择。

The code in a template is merely a 'pattern' known to the compiler. The compiler won't compile the constructors cola<float>::cola(...)and cola<string>::cola(...)until it is forced to do so. And we must ensure that this compilation happens for the constructors at leastonce in the entire compilation process, or we will get the 'undefined reference' error. (This applies to the other methods of cola<T>also.)

模板中的代码只是编译器已知的“模式”。编译器不会编译构造函数cola<float>::cola(...)cola<string>::cola(...)直到它被迫这样做。并且我们必须确保在整个编译过程中至少对构造函数进行一次编译,否则我们将得到“未定义引用”错误。(这适用于cola<T>also的其他方法。)

Understanding the problem

了解问题

The problem is caused by the fact that main.cppand cola.cppwill be compiled separately first. In main.cpp, the compiler will implicitlyinstantiate the template classes cola<float>and cola<string>because those particular instantiations are used in main.cpp. The bad news is that the implementations of those member functions are not in main.cpp, nor in any header file included in main.cpp, and therefore the compiler can't include complete versions of those functions in main.o. When compiling cola.cpp, the compiler won't compile those instantiations either, because there are no implicit or explicit instantiations of cola<float>or cola<string>. Remember, when compiling cola.cpp, the compiler has no clue which instantiations will be needed; and we can't expect it to compile for everytype in order to ensure this problem never happens! (cola<int>, cola<char>, cola<ostream>, cola< cola<int> >... and so on ...)

问题是由于main.cppcola.cpp将首先单独编译。在 中main.cpp,编译器将隐式实例化模板类,cola<float>并且cola<string>因为在main.cpp. 坏消息是,那些成员函数的实现不在main.cpp,也没有在包括在任何头文件main.cpp,因此,编译器可以不包括在这些功能完成版本main.o。编译时cola.cpp,编译器将不会编译这些实例要么,因为有没有暗示或明示的实例cola<float>cola<string>。记住,编译时cola.cpp,编译器不知道需要哪些实例化;我们不能指望它为每种类型编译以确保这个问题永远不会发生!( cola<int>, cola<char>, cola<ostream>, cola< cola<int> >... 等等...)

The two answers are:

两个答案是:

  • Tell the compiler, at the end of cola.cpp, which particular template classes will be required, forcing it to compile cola<float>and cola<string>.
  • Put the implementation of the member functions in a header file that will be included everytime any other 'translation unit' (such as main.cpp) uses the template class.
  • 在 的末尾告诉编译器cola.cpp需要哪些特定的模板类,强制编译cola<float>cola<string>.
  • 将成员函数的实现放在一个头文件中,每次任何其他“翻译单元”(例如main.cpp)使用模板类时都会包含该头文件。

Answer 1: Explicitly instantiate the template, and its member definitions

答案 1:显式实例化模板及其成员定义

At the endof cola.cpp, you should add lines explicitly instantiating all the relevant templates, such as

结束cola.cpp,你应该添加行明确实例化所有相关的模板,如

template class cola<float>;
template class cola<string>;

and you add the following two lines at the end of nodo_colaypila.cpp:

并在末尾添加以下两行nodo_colaypila.cpp

template class nodo_colaypila<float>;
template class nodo_colaypila<std :: string>;

This will ensure that, when the compiler is compiling cola.cppthat it will explicitly compile all the code for the cola<float>and cola<string>classes. Similarly, nodo_colaypila.cppcontains the implementations of the nodo_colaypila<...>classes.

这将确保在编译器编译时cola.cpp,它将显式编译cola<float>cola<string>类的所有代码。同样,nodo_colaypila.cpp包含nodo_colaypila<...>类的实现。

In this approach, you should ensure that all the of the implementation is placed into one .cppfile (i.e. one translation unit) and that the explicit instantation is placed after the definition of all the functions (i.e. at the end of the file).

在这种方法中,您应该确保所有的实现都放在一个.cpp文件中(即一个翻译单元),并且显式实例化放在所有函数的定义之后(即在文件的末尾)。

Answer 2: Copy the code into the relevant header file

答案2:将代码复制到相关的头文件中

The common answer is to move all the code from the implementation files cola.cppand nodo_colaypila.cppinto cola.hand nodo_colaypila.h. In the long run, this is more flexible as it means you can use extra instantiations (e.g. cola<char>) without any more work. But it could mean the same functions are compiled many times, once in each translation unit. This is not a big problem, as the linker will correctly ignore the duplicate implementations. But it might slow down the compilation a little.

常见的答案是从实现文件把所有的代码cola.cppnodo_colaypila.cppcola.hnodo_colaypila.h。从长远来看,这更灵活,因为这意味着您可以使用额外的实例化(例如cola<char>)而无需更多工作。但这可能意味着相同的函数被编译多次,在每个翻译单元中编译一次。这不是什么大问题,因为链接器会正确地忽略重复的实现。但它可能会稍微减慢编译速度。

Summary

概括

The default answer, used by the STL for example and in most of the code that any of us will write, is to put all the implementations in the header files. But in a more private project, you will have more knowledge and control of which particular template classes will be instantiated. In fact, this 'bug' might be seen as a feature, as it stops users of your code from accidentally using instantiations you have not tested for or planned for ("I know this works for cola<float>and cola<string>, if you want to use something else, tell me first and will can verify it works before enabling it.").

例如,STL 使用的默认答案以及我们任何人将编写的大多数代码中的默认答案是将所有实现放在头文件中。但在一个更私密的项目中,您将拥有更多的知识和控制权,了解哪些特定的模板类将被实例化。事实上,这个“错误”可能被视为一项功能,因为它可以阻止您的代码用户意外使用您尚未测试或计划的实例(“我知道这适用于cola<float>并且cola<string>,如果您想使用其他东西,先告诉我,我会在启用它之前验证它是否有效。”)。

Finally, there are three other minor typos in the code in your question:

最后,您问题中的代码还有另外三个小错别字:

  • You are missing an #endifat the end of nodo_colaypila.h
  • in cola.h nodo_colaypila<T>* ult, pri;should be nodo_colaypila<T> *ult, *pri;- both are pointers.
  • nodo_colaypila.cpp: The default parameter should be in the header file nodo_colaypila.h, not in this implementation file.
  • #endif在 nodo_colaypila.h 的末尾丢失了
  • 在 cola.h 中nodo_colaypila<T>* ult, pri;应该是nodo_colaypila<T> *ult, *pri;- 两者都是指针。
  • nodo_colaypila.cpp:默认参数应该在头文件中nodo_colaypila.h,而不是在这个实现文件中。

回答by Alok Save

You will have to define the functions inside your header file.
You cannot separate definition of template functions in to the source file and declarations in to header file.

您必须在头文件中定义函数。
您不能将模板函数的定义在源文件中和在头文件中的声明分开。

When a template is used in a way that triggers its intstantation, a compiler needs to see that particular templates definition. This is the reason templates are often defined in the header file in which they are declared.

当以触发其实例化的方式使用模板时,编译器需要查看该特定模板定义。这就是模板通常在声明它们的头文件中定义的原因。

Reference:
C++03 standard, § 14.7.2.4:

参考:
C++03 标准,第 14.7.2.4 节:

The definitionof a non-exported function template, a non-exported member function template, or a non-exported member function or static data member of a class template shall be present in every translation unitin which it is explicitly instantiated.

定义非导出函数模板,非导出的成员函数模板,或者非导出成员函数或静态数据成员的类模板的应存在于每个翻译单元在其被显式实例。

EDIT:
To clarify the discussion on the comments:
Technically, there are three ways to get around this linking problem:

编辑:
澄清对评论的讨论:
从技术上讲,有三种方法可以解决这个链接问题:

  • To move the definition to the .h file
  • Add explicit instantiations in the .cppfile.
  • #includethe .cppfile defining the template at the .cppfile using the template.
  • 将定义移动到 .h 文件
  • .cpp文件中添加显式实例化。
  • #include在使用模板的.cpp文件中定义模板的.cpp文件。

Each of them have their pros and cons,

各有优缺点,

Moving the defintions to header files may increase the code size(modern day compilers can avoid this) but will increase the compilation time for sure.

将定义移动到头文件可能会增加代码大小(现代编译器可以避免这种情况),但肯定会增加编译时间。

Using the explicit instantiation approach is moving back on to traditional macro like approach.Another disadvantage is that it is necessary to know which template types are needed by the program. For a simple program this is easy but for complicated program this becomes difficult to determine in advance.

使用显式实例化方法正在回到传统的类似宏的方法。另一个缺点是需要知道程序需要哪些模板类型。对于简单的程序,这很容易,但对于复杂的程序,这很难提前确定。

While including cpp files is confusing at the same time shares the problems of both above approaches.

虽然包含 cpp 文件令人困惑,但同时也存在上述两种方法的问题。

I find first method the easiest to follow and implement and hence advocte using it.

我发现第一种方法最容易遵循和实施,因此提倡使用它。

回答by Liam M

This link explains where you're going wrong:

这个链接解释了你哪里出错了:

[35.12] Why can't I separate the definition of my templates class from its declaration and put it inside a .cpp file?

[35.12] 为什么我不能将我的模板类的定义与其声明分开并将它放在一个 .cpp 文件中?

Place the definition of your constructors, destructors methods and whatnot in your header file, and that will correct the problem.

将构造函数、析构函数方法等的定义放在头文件中,这将解决问题。

This offers another solution:

这提供了另一种解决方案:

How can I avoid linker errors with my template functions?

如何避免模板函数出现链接器错误?

However this requires you to anticipate how your template will be used and, as a general solution, is counter-intuitive. It does solve the corner case though where you develop a template to be used by some internal mechanism, and you want to police the manner in which it is used.

但是,这需要您预测模板的使用方式,作为一般解决方案,这是违反直觉的。它确实解决了极端情况,尽管您开发了一个模板以供某些内部机制使用,并且您想监管它的使用方式。