Что означают эти ошибки связывания? (C ++) (MSVC ++) - PullRequest
1 голос
/ 13 мая 2009

РЕДАКТИРОВАТЬ: Когда я объединяю 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 ]

10 голосов
/ 14 мая 2009

Возможно прочитать ошибки компоновщика с небольшим усилием. Давайте попробуем:

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, принадлежит списку, называется 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 s. Поскольку он не видит 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>;

Это указывает компилятору специально генерировать код для этой специализации, даже если он не используется иным образом в этом модуле компиляции. И, очевидно, этот подход полезен, только если вы заранее знаете, какие специализации понадобятся.

Edit:

Похоже, вы никогда не определяете функцию clear(), которая вызывается из деструктора. Это является причиной последней ошибки компоновщика, которую вы получаете после включения всего в заголовок.

1 голос
/ 14 мая 2009

Еще одна догадка: как вы определили функции-члены шаблона класса? Они inline д? У вас есть определение в отдельном файле реализации? В противном случае вы можете столкнуться с этой проблемой: FAQ 35.16 .

0 голосов
/ 14 мая 2009

Реализовали ли вы методы списка классов шаблонов в list_dec.h или в list_dec.cpp?

Если нет, вам следует переместить их, потому что реализации метода шаблона всегда должны быть видны для всех точек использования.

0 голосов
/ 14 мая 2009

В моем опыте программирования, когда у меня есть шаблонный класс, я следую следующему

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

#include "MyListImplementation.h"
#endif

Фактический код реализации также содержится в файле ".h", а не в файле CPP, я точно не знаю, почему это происходит, но я знаю, что таким образом вы можете заставить его работать.

0 голосов
/ 14 мая 2009

Моя интуиция подсказывает мне, что вы, вероятно, неправильно объявляете специализацию шаблона. Если ваша реализация класса списка обобщена, вам нужно сообщить компилятору, с какими типами вы будете его использовать. Просто поставив строку

template class list<int, 100>;

внизу list_def.cpp, скорее всего, решит вашу проблему.

0 голосов
/ 14 мая 2009

Вы уверены, что действительно компилируете и связываете с list_def.cpp (где, я думаю, ваш шаблон списка <> определен)? Компоновщик не может найти ни одного из трех членов, которые вы используете (деструктор, return_current () или конструктор по умолчанию.

...