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

4

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

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

4个回答

4
函数模板(包括类模板的成员函数)的定义必须在.h文件中,以便它们出现在使用它们的每个cpp文件中。这就是模板的工作原理。您不能将定义放入cpp文件中。从技术上讲,有一个“export”关键字可以实现这一点,但由于几乎没有实现支持它,因此它在新标准中被移除。
阅读此内容:包含模型

你不能把定义放到cpp文件中。这是不正确的,你可以把它们放进去。 - iammilind
@imannilind:我知道,我的意思是它不会像你期望的那样链接。我已经包含了一个详细解释的链接。 - Armen Tsirunyan
2
@hkproj 更多阅读请参考C++ FAQ(该部分及其后续部分)。 - jonsca

1

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

  1. 将所有定义放在“.h”文件中
  2. 将定义放在“.cpp”文件中(用于代码分离),并#include该“.cpp”文件

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


0
考虑一个模板函数,它接受 T 并执行模数(%)操作,或者也可以执行简单加法(+)操作。
template <class T>
T GetT(T t1, T t2)
{
    return t1%t2;
}

你在这段代码中看不到任何错误。好的。当我传递两个整数时,它被编译:

GetT(10,20);

但是当我传递浮点/双精度时,它不会编译:

GetT(10.6, 20.5);

编译器将会发出以下错误信息:error C2296: '%' : 非法的左操作数类型 'double' 以及其他相关错误。 关键在于模板代码只有在您至少为特定数据类型实例化一次后才会被编译。模板代码保持不变 - 编译器不关心代码内部实际是什么。在您的情况下,CPP文件只是编译器已经忽略的文本文件 - 文件中所有内容都是如此。
话虽如此,当我使用运算符+而非运算符%时,它适用于所有基本数据类型,但是对于缺少operator + 的类则无效。在这种情况下,编译器将重新编译该数据类型(类)的模板代码。
有些情况下,编译器和链接器共同工作,以减少最终的二进制代码大小,当它们看到某些代码重复且对于所有/多个数据类型相同时。但那是另一个情况。

0

这是来自Nicolai Josutis的传奇之作,

C++ Templates: A complete Guide

模板被编译两次:

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

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


因此,对于模板而言,声明和定义应该保持在同一个头文件中,这样它们才能在使用它们的每个 CPP 中可见。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接