这些链接错误意味着什么? (C ++)(MSVC ++)

时间:2009-05-13 19:58:43

标签: c++ templates linker

编辑:当我将link_def.cpp与link_dec.h合并时,我只得到第一个错误,而不是第二个错误。

当我尝试编译一些代码时,我遇到了这些链接器错误: 代码:

#include"list_dec.h"
#include"catch.h"

int main()
{
    list<int, 100> L1;
    try
    {
        L1.return_current();
    }
    catch(err)
    {
        return -1;
    }
    return 0;
}

错误:

Linking...
main.obj : error LNK2019: unresolved external symbol "public: __thiscall list<int,100>::~list<int,100>(void)" (??1?$list@H$0GE@@@QAE@XZ) referenced in function __catch$_main$0
main.obj : error LNK2019: unresolved external symbol "public: int __thiscall list<int,100>::return_current(void)" (?return_current@?$list@H$0GE@@@QAEHXZ) referenced in function _main
main.obj : error LNK2019: unresolved external symbol "public: __thiscall list<int,100>::list<int,100>(void)" (??0?$list@H$0GE@@@QAE@XZ) referenced in function _main

如果有人需要list_dec.h,catch.h和list_def.cpp(列表类的定义)的代码,只需注释,如果它们无关紧要,我不想包含它们,因为它们非常大。

因为有人想看list_dec.h(我暂时与list_def.cpp合并) list_dec.h:

template<class T, size_t limit>
class list
{
public:
    //constructors
    list();
    list(const list<T, limit>& lst);
    ~list();

    //assignment operator
    void operator=(const list<T, limit>& lst);

    //append new items
    void append(const T& item);

    //clear the list
    void clear();

    //return current item
    T return_current();

    //test if item is last item
    bool last();

    //make current item head of the list
    void make_head();

    //move to the next item
    bool next();

    //interrogation
    size_t return_count();
    size_t return_limit();

protected:
    size_t count; //# of items in list
    size_t current; //current item
    T data[limit]; //array of elements of list

    //internal functions
    void copy(const list<T, limit>& lst);
};



//copier function
template<class T, size_t limit>
void list<T, limit>::copy(const list<T, limit>& lst)
{
    count = lst.count;
    current = lst.current;

    for(size_t n = 0; n < count; n++)
    {
        data[n] = lst.data[n];
    }
    return;
}

//constructor
template<class T, size_t limit>
inline list<T, limit>::list()
{
    count = 0;
    current = 0;
}

//copy constructor
template<class T, size_t limit>
inline list<T, limit>::list(const list<T, limit>& lst)
{
    copy(lst);
}

//assignment operator
template<class T, size_t limit>
inline void list<T, limit>::operator=(const list<T, limit>& lst)
{
    clear();
    copy(lst);
    return;
}

//destructor
template<class T, size_t limit>
inline list<T, limit>::~list()
{
    clear();
}

//append function
template<class T, size_t limit>
void list<T, limit>::append(const T& item)
{
    if(count == limit)
    {
        throw CX_OVERFLOW;
    }
    data[count] = item;
    count++;
    return;
}

//return current item
template<class T, size_t limit>
T list<T, limit>::return_current()
{
    if(count == 0)
    {
        throw CX_NULL;
    }
    if(current == count)
    {
        throw CX_ATEND;
    }
    return data[current];
}

//test if <current> pointer is at tail
template<class T, size_t limit>
inline bool list<T, limit>::last()
{
    if(current == count)
    {
        return true;
    }
    else
    {
        return false;
    }
}

//set current pointer to head
template<class T, size_t limit>
inline void list<T, limit>::make_head()
{
    current = 0;
    return;
}

//set current pointer to next pointer in list
template<class T, size_t limit>
bool list<T, limit>::next()
{
    if(count == 0)
    {
        throw CX_NULL;
    }
    if(current == count)
    {
        throw CX_ATEND;
    }

    current++;

    if(current == count)
    {
        return false;
    }

    return true;
}

//interrogation functions
template<class T, size_t limit>
inline size_t list<T, limit>::return_count()
{
    return count;
}
template<class T, size_t limit>
inline size_t list<T, limit>::return_limit()
{
    return limit;
}

6 个答案:

答案 0 :(得分:10)

可以通过一些努力来阅读链接器错误。我们试一试:

main.obj : error LNK2019: unresolved external symbol "public: __thiscall list<int,100>::~list<int,100>(void)" (??1?$list@H$0GE@@@QAE@XZ) referenced in function __catch$_main$0
  • 错误发生在main.obj,即从main.cpp生成的目标文件。
  • 问题是“未解决的外部符号”。也就是说,您引用了预期在另一个编译单元中定义的符号,但链接器无法找到此定义。
  • 有问题的符号是公共成员函数(__thiscall是成员函数的调用约定。)
  • 现在我们来看一些非常有用的信息:list<int,100>::~list<int,100>(void)"告诉我们问题是类模板列表的析构函数,专门用于<int, 100>。这部分格式很好,非常简单。对于第二个错误,我们还会看到返回类型:int __thiscall list<int,100>::return_current(void)。也就是说,一个返回int的函数,使用__thiscall调用约定,属于list,被称为return_current,并且不带参数。
  • ??1?$list@H$0GE@@@QAE@XZ几乎可以被忽略。这是编译器损坏的符号名称,因此这是链接器在.obj文件中查找的名称。但由于链接器已经告诉我们符号的可读C ++名称,我们并不需要这个。 (除非你决定自己去挖掘.obj文件)。
  • 最后,它告诉我们在函数main中引用了符号。 (读取链接器错误时的一般规则是忽略你不理解的位。我不完全确定这里的“catch”部分意味着什么,但它与你在main中执行的异常处理有关。

所以你有它。第一个错误表明无法找到列表析构函数的定义。如果函数未在类定义中声明为内联,则应在另一个.cpp文件中定义。我假设这是你的list_def.cpp,并且该文件也被编译并传递给链接器。

然而,这引出了一个很好的模板问题。它们是编译时构造,并且必须在编译器为其发出代码之前实例化模板。在编译器的输出中,类模板list和专业化list<int, 84>都不存在代码,因为不使用该特化。编译器只生成实际需要的特化。

当编译器处理list_def.cpp时,似乎不需要没有专门化。它无法看到其他.cpp文件,例如main.cpp。它只看到当前正在编译的.cpp文件,以及它#include的任何内容。由于它看不到list<int, 100>,因此它不会为该特化生成任何代码,然后当目标文件传递给链接器时,它无法找到list<int, 100>符号的定义并发出错误。

通常的解决方案是在标题中内联定义类模板的所有成员。这样,该定义对包括头部在内的任何编译单元都是可见的,因此编译器可以创建所需的模板特化。

具体而言,以下内容将产生链接器错误:

// .h
template <int n>
class Foo {
  int Bar();
};

// .cpp
template <int n>
int Foo::Bar() {
  return n; // Error: This is not visible from other .cpp files
}

所以只需将.cpp内容移动到标题中:

// .h
template <int n>
class Foo {
  int Bar();
};

template <int n>
int Foo::Bar() {
  return n; // Error: This is will cause the function to be defined in every .cpp file that includes it, so you'll get a *different* linker error instead (multiple definitions)
}

但这是有效的,也是通常的解决方案

// .h
template <int n>
class Foo {
  int Bar() { return n; } // just define it here, inside the class definition, and it is implicitly inline, so it's ok that multiple .cpp files see it
};

或者:

// .h
template <int n>
class Foo {
  int Bar();
};

// still in .h
template <int n>
inline int Foo::Bar() { // explicitly marking it inline works too. Now the compiler knows that it might be defined in multiple .cpp files, and these definitions should be merged back together
  return n;
}

更不寻常但偶尔有用的解决方案是在定义它的编译单元中显式实例化模板。因此,在list_def.cpp中,在模板定义后添加以下行:

template class list<int, 100>;

这告诉编译器专门为该特化生成代码,即使它没有在此编译单元中使用。显然,只有事先知道需要哪些专业化,这种方法才有用。

修改

看起来你永远不会定义从析构函数调用的clear()函数。这就是在标题中包含所有内容之后发生最终链接器错误的原因。

答案 1 :(得分:1)

另一种预感:你如何定义类模板的成员函数?他们是inline d?你有一个单独的实现文件中的定义?如果您这样做,可能会遇到此问题:FAQ 35.16

答案 2 :(得分:0)

你确定你真的在编译和链接list_def.cpp(我想你的列表&lt;&gt;模板已定义)?链接器找不到您正在使用的三个成员中的任何一个(析构函数,return_current()或默认构造函数。

答案 3 :(得分:0)

我的直觉告诉我你可能没有正确宣布模板专业化。如果您的列表类实现是通用的,那么您需要告诉编译器您将使用它的类型。只是把这行

template class list<int, 100>;
然后,list_def.cpp底部的

很可能会解决您的问题。

答案 4 :(得分:0)

在我的编程经验中,每当我有一个模板化课程时,我都会遵循以下

#ifndef MY_LIST
#define MY_LIST
class List< [...] >
{ 
[ ...]
};

#include "MyListImplementation.h"
#endif

实际的实现代码也是“.h”文件而不是CPP文件,我不确定为什么会发生这种情况,但我知道这样你可以让它工作。

答案 5 :(得分:0)

您是否将模板类列表的方法实现到list_dec.h或list_dec.cpp中?

如果不是,则应移动它们,因为模板方法实现必须始终在所有使用点都可见。