【问题标题】:C++ compile error on Visual Studio 10Visual Studio 10 上的 C++ 编译错误
【发布时间】:2011-07-16 11:39:00
【问题描述】:

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

如您所见,这是一个非常简单的使用链表的队列实现...

【问题讨论】:

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


    【解决方案1】:

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

    阅读:The inclusion model

    【讨论】:

    • You cant put the definitions into a cpp file。这不正确,你可以放它们。
    • @imannilind:我知道,我的意思是它不会像你期望的那样链接。我已经包含了一个详细解释它的链接
    • 谢谢。我不知道。 ;-)
    • @hkproj 进一步阅读C++ FAQ(该部分和其他部分)。
    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

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

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

      GetT(10,20);
      

      但是当我通过 float/double 时它不会编译:

      GetT(10.6, 20.5);
      

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

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

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

      【讨论】:

        【解决方案4】:

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

        C++ 模板:完整指南

        模板编译两次:

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

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


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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-07-06
          • 1970-01-01
          • 2010-10-09
          • 1970-01-01
          • 2019-11-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多