Visual Studio 10上的C ++编译错误

时间:2011-07-16 11:39:01

标签: c++ visual-studio-2010 templates compiler-errors

使用Visual Studio编译这个简单程序时出现以下错误:

error LNK2019: unresolved external symbol "public: void __thiscall CoList<int>::enqueue(int)" (?enqueue@?$CoList@H@@QAEXH@Z) referenced in function _main

error LNK2019: unresolved external symbol "public: virtual __thiscall CoList<int>::~CoList<int>(void)" (??1?$CoList@H@@UAE@XZ) referenced in function _main

error LNK2019: unresolved external symbol "public: int __thiscall CoList<int>::dequeue(void)" (?dequeue@?$CoList@H@@QAEHXZ) referenced in function _main

error LNK2019: unresolved external symbol "public: int __thiscall CoList<int>::count(void)" (?count@?$CoList@H@@QAEHXZ) referenced in function _main

error LNK2019: unresolved external symbol "public: __thiscall CoList<int>::CoList<int>(void)" (??0?$CoList@H@@QAE@XZ) referenced in function _main

error LNK1120: 5 unresolved externals

我的程序非常简单。我不使用外部库,只使用'iostream'和'exception'标题......以下是完整代码:

CoList.h

#pragma once

#include "CoListItem.h"

template <class T>
class CoList
{

public:

    CoList();
    virtual ~CoList();

    void enqueue(T value);
    T dequeue();
    T *peek();
    int count();

private:
    CoListItem<T> *m_root;
    int m_count;

};

CoListItem.h

#pragma once

template <class T>
class CoListItem
{

public:

    CoListItem();
    virtual ~CoListItem();

    T value;
    CoListItem *next;

};

CoList.cpp

#include "CoList.h"
#include <exception>

template <class T>
CoList<T>::CoList()
{
}

template <class T>
CoList<T>::~CoList()
{
}

template <class T>
void CoList<T>::enqueue(T value)
{
    if (this->m_root != NULL) {
        this->m_root = new CoListItem<T>();
        this->m_root->value = value;
        this->m_root->next = NULL;
    } else {
        CoListItem<T> *tempitem = new CoListItem<T>();
        tempitem->value = value;
        tempitem->next = this->m_root;

        this->m_root = tempitem;
    }

    this->m_count++;
}

template <class T>
T CoList<T>::dequeue()
{
    if (this->m_root == NULL) {
        throw std::exception();
    } else {
        T retval = this->m_root->value;
        CoListItem *next = this->m_root->next;
        delete this->m_root;
        this->m_root = next;

        return retval;
    }
}

template <class T>
T *CoList<T>::peek()
{
    if (this->m_root == NULL) {
        return NULL;
    } else {
        return *this->dequeue();
    }
}

template <class T>
int CoList<T>::count()
{
    return this->m_count;
}

CoListItem.cpp

#include "CoListItem.h"

template <class T>
CoListItem<T>::CoListItem()
{
}


template <class T>
CoListItem<T>::~CoListItem()
{
}

最后是主要功能:

#include <iostream>
#include "CoList.h"
#include "CoListItem.h"

using namespace std;

int main(int argc, char *argv[])
{
    CoList<int> list;

    for(int i = 0; i < 10; i++)
        list.enqueue(i);

    cout << "Count: " << list.count() << endl;

    for(int i = 0; i < 10; i++)
        cout << "Item: " << list.dequeue() << endl;

    cout << "Count: " << list.count() << endl;

    int wait = 0;
    cin >> wait;
}

正如您所看到的,它是一个使用链表的非常简单的Queue实现......

4 个答案:

答案 0 :(得分:4)

函数模板的定义(包括类模板的成员函数)必须位于.h文件中,以便它们出现在使用它们的每个cpp文件中。这就是模板的工作原理。您无法将定义放入cpp文件中。从技术上讲,有一个export关键字可以实现这一点,但由于几乎没有实现支持它,它在新标准中被删除。

阅读本文:The inclusion model

答案 1 :(得分:1)

template定义应该对使用它的代码可见。为此,

  1. 将所有定义放在“.h”文件
  2. 将定义放在“.cpp”文件中(用于代码分离)和 #include表示“.cpp”文件
  3. 例如,在您的情况下,您可以#include "CoList.cpp"而不是"CoList.h"。等等。

答案 2 :(得分:0)

直接来自 Nicolai Josutis的传奇书,

C ++模板:完整指南

模板编译两次:

  • 在没有实例化的情况下,检查模板本身的语法是否正确,这里会发现语法错误,例如分号。
  • 作为实例化时,检查模板代码以确保所有调用都有效。发现无效调用,例如不支持的函数调用。

这导致模板实践处理中的重要问题。当函数模板以触发实例化的方式使用时,编译器将(在某些时候)需要查看该模板定义。当函数声明足以编译其使用时,这会破坏函数的通常编译和链接区别。


因此,对于模板,声明和定义应保存在同一个头文件中,以便它们在使用它们的每个cpp中都可见。

答案 3 :(得分:0)

考虑一个模板函数,它接受T并执行模数(%)或简单的加法(+)。

template <class T>
T GetT(T t1, T t2)
{
    return t1%t2;
}

您在此代码中看到NO ERROR。好的。当我传递两个整数时,它会被编译:

GetT(10,20);

但是当我传递float / double时,WONT编译:

GetT(10.6, 20.5);

编译器将发出:error C2296: '%' : illegal, left operand has type 'double'和其他相关错误。 关键是模板代码在您为特定数据类型至少实例化一次之前不会被编译。模板代码保持垃圾 - 编译器不关心代码中的实际内容。在您的情况下,CPP只是编译器忽略的文本文件 - 所有这些。

话虽如此,当我使用运算符+而不是运算符%时,它将适用于所有基本数据类型,但不适用于缺少operator +的类。在这种情况下,编译器将再次编译该数据类型(类)的模板内容。

有些情况下编译器和链接器协同工作以减少最终的二进制代码大小,当他们看到某些代码是重复的并且对于所有/多个数据类型都是相同的。但那是一个不同的案例。