Проблема с реализацией шаблона
Код длинный, поэтому здесь не привожу, см. вложение.
Среда - MSVC 2005.
Текст ошибки:
1>file_work.obj : error LNK2019: unresolved external symbol "class DataFile & __cdecl operator<<(class DataFile &,class SerializedList<class Distribution::FileInfo> const &)" (??6@YAAAVDataFile@@AAV0@ABV?$SerializedList@VFileInfo@Distribution@@@@@Z) referenced in function "class DataFile & __cdecl operator<<(class DataFile &,class Distribution const &)" (??6@YAAAVDataFile@@AAV0@ABVDistribution@@@Z)
1>file_work.obj : error LNK2019: unresolved external symbol "class DataFile & __cdecl operator<<(class DataFile &,class SerializedList<struct Distribution::StartAddr> const &)" (??6@YAAAVDataFile@@AAV0@ABV?$SerializedList@UStartAddr@Distribution@@@@@Z) referenced in function "class DataFile & __cdecl operator<<(class DataFile &,class Distribution const &)" (??6@YAAAVDataFile@@AAV0@ABVDistribution@@@Z)
Ну и т. д.
И проблема собственно тоже из-за запутывания кода.
Я вообще не люблю friend-ы, а уж шаблонные тем более.
А решение такое:
{
......
template<class U> friend DataFile& operator << (DataFile &df, SerializedList<U> const & data);
template<class U> friend DataFile& operator >> (DataFile &df, SerializedList<U>& data);
};
Думаю, разберешься что и почему.
Ещё бросается в глаза работа с областями памяти, типа таких вещей:
почему бы не использовать vector<char> ?
вот здесь видимо ошибка
т.к. BYTE checksum[8];
вот это некрасиво смотриться:
Ещё ошибка вот здесь:
{
DWORD sz = (DWORD)size();
df.Write (sizeof(DWORD), &sz);
const_iterator end_iter = end();
for (const_iterator iter = begin(); iter != end_iter; ++iter)
DataFile << *iter;
};
{
......
template<class U> friend DataFile& operator << (DataFile &df, SerializedList<U> const & data);
template<class U> friend DataFile& operator >> (DataFile &df, SerializedList<U>& data);
};
Мы сейчас провернули такую махинацию, что объявили ЛЮБУЮ реализацию оператора дружественной ЛЮБОЙ реализации класса (т. е. напр. class<int> будет дружественен operator<< <char> и т. п.), а нам-то в принципе это не обязательно было... Странно всё это... Или по стандарту так положено?
Спасибо за подсказку, эту DataFile << *iter я даже не приметил, такую глупость сморозил.
По поводу остальных моментов, с auto_ptr согласен, он не для массивов, и константы типа 8 и 16 надо объявлять #define, а вот df.Write(sizeof(DWORD), &addr.ip) непонятно чем вам не понравилось. Просто проект пока в сыром виде, можно сказать - просто набросок, и будет дорабатываться. И скорее всего выложу где-нибудь как open source, пусть студенты учатся :)
Мы сейчас провернули такую махинацию, что объявили ЛЮБУЮ реализацию оператора дружественной ЛЮБОЙ реализации класса (т. е. напр. class<int> будет дружественен operator<< <char> и т. п.), а нам-то в принципе это не обязательно было... Странно всё это... Или по стандарту так положено?
Вся сложность в том, что friend является объявлением (кстати может быть и определением). Т.е. написав во friend функцию, ты её объявил, но объявил то ты её не как шаблонную (в её определении нет упоминания, что это шаблонная функция). А т.к. в C++ есть перегрузка функций,то у тебя получается две функции: шаблонная и нешаблонная. Причем определение (реализация) только у шаблонной, поэтому линкер и кричит, что не смог найти реализацию для НЕшаблонной функции.
Другими словами:
template<typename T> DataFile& operator >> (DataFile &df, SerializedList<T>& data)
и
friend DataFile& operator << (DataFile &df, SerializedList<T> const & data)
это две разных функции, у одной есть реализация а у другой нет.
Как это исправить?
Вариант первый: сделать реализацию у нешаблонной функции. Причем, как сказал уже, можно это сделать прямо в объявлении friend.
{
....
friend DataFile& operator << (DataFile &df, SerializedList<T> const & data)
{
data.InnerSave (df);
return df;
}
friend DataFile& operator >> (DataFile &df, SerializedList<T>& data)
{
data.InnerLoad (df);
return df;
}
Вариант второй: указать при объявлении друга, что он - шаблонная функция.
Щас будут финты ушами:
template<typename T>
DataFile& operator << (DataFile &df, SerializedList<T> const & data)
{
data.InnerSave (df);
return df;
};
template<typename T>
DataFile& operator >> (DataFile &df, SerializedList<T>& data)
{
data.InnerLoad (df);
return df;
};
template <typename T> class SerializedList : public list<T>
{
....
friend DataFile& operator<< <> (DataFile &df, SerializedList<T> const & data);
friend DataFile& operator >> <> (DataFile &df, SerializedList<T>& data);
};
По поводу остальных моментов, с auto_ptr согласен, он не для массивов, и константы типа 8 и 16 надо объявлять #define, а вот df.Write(sizeof(DWORD), &addr.ip) непонятно чем вам не понравилось.
Тем не понравилось, что если изменится размерность члена класса, то участки кода станут не корректными.
Для того, чтоб такого не случалось, можно завязываться на размер члена класса, а не конкретного типа:
аналогично здесь:
....
df.Write(sizeof(Distribution::signature), Distribution::signature);