Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.118.126.11] |
|
Сообщ.
#1
,
|
|
|
Добрый день, уважаемые программисты. Не так давно я начал перелазить с Delphi на C++. Поначалу меня охватила эйфория, когда я понял, какие ништяки могу писать, но потом она сошла, так как эти самые ништяки никак не компилятся. Код:
//LazyString.h namespace Lazy{ template<typename T> class BasicString { public: BasicString operator= (const BasicString& right); friend BasicString operator+(const BasicString& left, const BasicString& right); friend bool operator==(const BasicString& left, const BasicString& right); friend bool operator!=(const BasicString& left, const BasicString& right); } } //LazyString.cpp #include "LazyString.h" template<typename T> Lazy::BasicString<T> Lazy::BasicString<T>::operator= (const Lazy::BasicString<T>& right) {}; template<typename T> Lazy::BasicString<T> Lazy::operator+(const Lazy::BasicString<T> & left, const Lazy::BasicString<T> & right) {}; template<typename T> bool Lazy::operator==(const Lazy::BasicString<T> &left, const Lazy::BasicString<T> &right) {}; template<typename T> bool Lazy::operator!=(const Lazy::BasicString<T>& left, const Lazy::BasicString<T>& right) {}; Эти функции сгенерировала VS. Без проблем только присваивание, на остальных ошибки типа "+ не явлется членом Lazy". Убираю "Lazy::" - VS подчёркивает функции зелёным, говорит, что нет определения, а при компиляции "ссылка на неразрешенный символ". Я так понимаю, компилятор не находит определения. Думаю, можно было бы объявить и определить в одном месте, но хотелось бы сделать как положено. Подскажите, пожалуйста, как. |
Сообщ.
#2
,
|
|
|
Не считая отсутствующие return в операторах, что, как я подозреваю, является всё-таки не ошибкой, а следствием чрезмерного упрощения примера, operator= должен возвращать ссылку, а не значение, иначе каждый раз будет вызываться конструктор копии, да и семантический смысл operator= изменится.
Но это не главное. С друзьями ты явно не дружишь. Не ты первый, не ты, увы, последний, я уверен. Добавлено По порядку. Я практически уверен, что у тебя компилятор видит объявление вроде вот того BasicString<T>::operator+() впервые внутри класса. Т.е. до этого никаких их объявлений не было. Ведь так? Инфа 100%? Давай, ставь себя на место компилятора: Сам догадаешься, что из этого следует? Добавлено Если не догадался, продолжаем. Полная сигнатура оператора формируется на основе текущей области видимости, но т.к. это область видимости класса, но оператор друг, а следовательно не метод, то в его сигнатуру не будет входить область видимости BasicString<>, но будет входить как некое имя, квалифицирующее его параметры. Т.е. фактически ты получишь что-то типа template<typename T> BasicString<T> operator+(const BasicString<T>& left, const BasicString<T>& right); Добавлено Второй вопрос: как теперь определить этот оператор? Тут можно долго не думать: никак. Невозможно определить нешаблонную функцию с зависимым однако именем чего бы там ни было от какого-то там шаблона. Ты ниже пытаешься его определить, но это не тот operator(), который вроде бы друг, т.к. он шаблонный, а тебе нужно определить его как нешаблонный. В результате компилятор видит некое определение, но оно нигде в программе не используется, т.к. вызывается не оно. Для друга же компилятор определения не видит, поэтому просто оставляет внешнюю ссылку для заполнения линкером, вдруг оно в других единицах трансляции есть. Линкер тоже ничего не видит, и потому в итоге ругается. Добавлено Да, с друзьями вот так. Объявление сущности, чтобы она стала известна компилятору, и объявление её другом другой сущности, чтобы компилятор знал, что у неё есть особые полномочия – это разные вещи. И хоть они могут быть совмещены в одном месте, лучше этого не делать. Ведь по сути твой оператор, хоть и нешаблонный, всё равно зависит от параметра шаблона BasicString, а значит каждый BasicString с уникальным T, получит собственного друга operator+(). И их будет много, как было бы много и шаблонных operator+(). Добавлено Как решить проблему. Тут два способа. Вот простой. Совместить определение с объявлением: namespace Lazy{ template<typename T> class BasicString { /* ... */ friend BasicString operator+(const BasicString& left, const BasicString& right) { /* ... */ return /* ...*/; } /* ... */ }; } Разделить первое объявление operator+ и его объявление другом: namespace Lazy { // объявление шаблонного класса, без него не скомпилится последующий прототип template<typename T> class BasicString; // объявление будущего друга template<typename T> BasicString<T> operator+(const BasicString<T>& left, const BasicString<T>& right); // определение шаблонного класса template<typename T> class BasicString { /* ... */ // ранее объявленный шаблонный оператор объявить другом BasicString friend BasicString operator+<T>(const BasicString& left, const BasicString& right); /* ... */ }; } Добавлено Теперь компилятор работает так: Добавлено Цитата Qraizer @ Пожалуй, надо пояснить. В Стандарте чёрным по-англицки написано, что при прочих равных условиях нешаблонные сущности имеют приоритет при разрешении перегрузки. Тут как раз такая ситуация: у компилятора две сигнатуры, одна суть нешаблонный "друг", вторая обычная шаблонная функция. Перегрузку молча выигрывает друг, для которого нет определения. Шаблонная всегда будет проигрывать, поэтому несмотря на имеющееся определение, она бесполезна. Ты ниже пытаешься его определить, но это не тот operator(), который вроде бы друг, т.к. он шаблонный, а тебе нужно определить его как нешаблонный. В результате компилятор видит некое определение, но оно нигде в программе не используется, т.к. вызывается не оно. |
Сообщ.
#3
,
|
|
|
Цитата k.sovailo @ Без проблем только присваивание, на остальных ошибки типа "+ не явлется членом Lazy". Убираю "Lazy::" - VS подчёркивает функции зелёным, говорит, что нет определения, а при компиляции "ссылка на неразрешенный символ". А это точно необходимо, чтобы функции friend BasicString operator+(const BasicString& left, const BasicString& right); friend bool operator==(const BasicString& left, const BasicString& right); friend bool operator!=(const BasicString& left, const BasicString& right); обязательно имели доступ к закрытым членам класса "BasicString" ? Конечно, технология использования неких функций как "friend" важна сама по себе, но не исключено, что в этом конкретном случае это и не нужно. |
Сообщ.
#4
,
|
|
|
Это другой вопрос, ЫукпШ. Подозреваю, что знаю, о чём будет следующая тема.
|
Сообщ.
#5
,
|
|
|
Qraizer, большое спасибо за такой развёрнутый ответ, не ожидал даже. Прочитал пару раз, вроде понял. Однако написать рабочий код мне это не помогло. При вызове любых шаблонных функций из maon.cpp (как методов, так и свободных функций) ошибка линкера. Почитал в интернете, что нельзя делать определение шаблонных функций в другом файле. Перенёс всё из LazyString.cpp в LazyString.h - таки да, заработало.
ЫукпШ, нет, не точно. Я действительно не знаю альтернатив. |
Сообщ.
#6
,
|
|
|
Цитата k.sovailo @ Вот. Именно эту проблему я и ванговал. На самом деле можно, но правильно это оформить в коде непросто, проще все определения шаблонов вынести в заголовки. Почитал в интернете, что нельзя делать определение шаблонных функций в другом файле. |