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

C++ compile error on Visual Studio 10

本文关键字:编译 错误 C++ 上的 Studio Visual      更新时间:2023-10-16

我在使用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"answers"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;
}

正如您所看到的,这是一个使用链表的非常简单的队列实现。。。

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

阅读:包含模型

template定义应该对使用它的代码可见

  1. 将所有定义放在".h"文件中
  2. 将定义放在".cpp"文件中(用于代码分离(,然后#include那个".cpp"文件

例如,在您的情况下,您可以使用#include "CoList.cpp"而不是"CoList.h"。等等。

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

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

您可以在该代码中看到"无错误"。好吧当我通过两个int时,它被编译:

GetT(10,20);

但是当我通过float/double时,WONT编译:

GetT(10.6, 20.5);

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

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

在某些情况下,当编译器和链接器发现某些代码是重复的,并且对于所有/几种数据类型都是相同的时,它们会协同工作以减少最终二进制代码的大小。但情况不同。

这直接来自Nicolai Josutis的传奇书籍

C++模板:完整指南

模板编译两次:

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

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


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