Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Проблема с реализацией шаблона

350
25 мая 2010 года
cheburator
589 / / 01.06.2006
Есть шаблонные операторы << и >>, линкер ругается на unresolved external, т. е. видимо конкретную реализацию шаблона компилятор не скомпилил по какой-то непонятной мне причине.
Код длинный, поэтому здесь не привожу, см. вложение.
Среда - 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)

Ну и т. д.
14K
25 мая 2010 года
Fenja
138 / / 20.03.2009
cheburator насколько я помню, если в проекте несколько файлов, то реализация шаблонных методов должна быть в хидере, а не в .cpp
3
25 мая 2010 года
Green
4.8K / / 20.01.2000
Ну вообще, весьма запутанный код. ИМХО его можно было сделать и по-проще.
И проблема собственно тоже из-за запутывания кода.
Я вообще не люблю friend-ы, а уж шаблонные тем более.
А решение такое:
 
Код:
template <typename T> class SerializedList : public list<T>
{
    ......
    template<class U> friend DataFile& operator << (DataFile &df, SerializedList<U> const & data);
    template<class U> friend DataFile& operator >> (DataFile &df, SerializedList<U>& data);
};

Думаю, разберешься что и почему.

Ещё бросается в глаза работа с областями памяти, типа таких вещей:
 
Код:
auto_ptr<char> tmp_fname(new char[fnlen+1]);

почему бы не использовать vector<char> ?

вот здесь видимо ошибка
 
Код:
df.Write(sizeof(BYTE)*16, fi.checksum);

 
Код:
memcpy(fi.checksum, tmp_checksum, sizeof(BYTE)*16);

т.к. BYTE checksum[8];

вот это некрасиво смотриться:
 
Код:
df.Write(16, Distribution::signature);

 
Код:
df.Read(16, sig_buf);

 
Код:
df.Write(sizeof(DWORD), &addr.ip);


Ещё ошибка вот здесь:
 
Код:
void InnerSave (DataFile &df) const
    {
        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;
    };
350
25 мая 2010 года
cheburator
589 / / 01.06.2006
Цитата: Green

 
Код:
template <typename T> class SerializedList : public list<T>
{
    ......
    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, пусть студенты учатся :)

3
25 мая 2010 года
Green
4.8K / / 20.01.2000
Цитата: cheburator

Мы сейчас провернули такую махинацию, что объявили ЛЮБУЮ реализацию оператора дружественной ЛЮБОЙ реализации класса (т. е. напр. 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.

Код:
template <typename T> class SerializedList : public list<T>
{
    ....
    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> class SerializedList;

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);
};



Цитата: cheburator

По поводу остальных моментов, с auto_ptr согласен, он не для массивов, и константы типа 8 и 16 надо объявлять #define, а вот df.Write(sizeof(DWORD), &addr.ip) непонятно чем вам не понравилось.


Тем не понравилось, что если изменится размерность члена класса, то участки кода станут не корректными.
Для того, чтоб такого не случалось, можно завязываться на размер члена класса, а не конкретного типа:

 
Код:
df.Write(sizeof(addr.ip), &addr.ip)

аналогично здесь:
 
Код:
const char* Distribution::signature[16] = "UnitedFilesData";
....
df.Write(sizeof(Distribution::signature), Distribution::signature);
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог