Russian Qt Forum
Май 02, 2024, 08:33 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Перегрузка бинарных операторов для шаблонного класса  (Прочитано 12436 раз)
schmidt
Гость
« : Октябрь 29, 2013, 00:36 »

Доброй ночи, уважаемые,

Перегруженные бинарные операторы для обычных классов полезно делать вне класса, объявляя их как friend, чтобы компилятор мог справиться с выражением

Код:
Type1_object + Type2_object

независимо от порядка операндов.

Тем же путем иду сейчас при разработке шаблонного класса, но сталкиваюсь с проблемами - компилятор не может подхватить реализацию оператора вне класса:

Код:
template <class T>
MyVector {
/*...*/
class iterator:
public:
  template <class C>
  friend const iterator& operator+(const iterator& iter, size_type step);
};
};

template <class T>
const typename AtdVector<T>::iterator& operator+(const typename AtdVector<T>::iterator& iter,
                                                 typename AtdVector<T>::size_type step) {
    /*...*/
}

Но при использовании его в коде компилятор жалуется, что не может найти подходящий оператор для вызова с такими аргументами

Цитировать
undefined reference to `operator+(AtdVector<int>::iterator const&, unsigned int)'

а также указывает на существующую реализацию оператора  в качестве кандидата

Цитировать
candidates are:
template<class T> const typename AtdVector<T>::iterator& operator+(const typename AtdVector<T>::iterator&, typename AtdVector<T>::size_type)

note:   template argument deduction/substitution failed:

note:   couldn't deduce template parameter 'T'

Почесав в затылке, я понял, что оператор при такой реализации оказался отдельной шаблонной функцией, не зависящей от исходного шаблонного класса. Манера использования шаблонных функций предписывает вызывать их так:

Код:
templateFunction<Type>(...);

Что совсем не вписывается в привычную работу с операторами. Есть ли варианты, как можно укротить шаблоны и добиться для них привычного использования бинарных операторов в стиле "a+b и ничего лишнего"?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #1 : Октябрь 29, 2013, 09:41 »

Код:
  template <class C>
  friend const iterator& operator+(const iterator& iter, size_type step);
Мои компиляторы такого не пропускают - нет аргуметов зависящих от "C"

Код:
typename AtdVector<T>::size_type step
Вот после таких экзерцисов утверждение "С++ монструозный язык" не кажется столь уж абсурдным  Плачущий  А если это еще воспринимается как бооольшая вумность .....
Записан
mutineer
Гость
« Ответ #2 : Октябрь 29, 2013, 10:08 »

Цитировать
undefined reference to `operator+(AtdVector<int>::iterator const&, unsigned int)'

А почему компилятор считает второй параметр интом?
Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #3 : Октябрь 29, 2013, 22:01 »

Код:
template <class T>
MyVector {          // <---- ошибка синтаксиса: непонятный символ: MyVector
/*...*/
class iterator: // <---- ошибка синтаксиса: похоже на объявление класса, за которым должно последовать наследование.
public:                       
  template <class C>
  friend const iterator& operator+(const iterator& iter, size_type step);
};
};

template <class T>
const typename AtdVector<T>::iterator& operator+(const typename AtdVector<T>::iterator& iter, <---- ошибка синтаксиса: неопознанный символ  AtdVector
                                                 typename AtdVector<T>::size_type step) {
    /*...*/
}



Для начала представьте полностью компилирующийся код, который содержит объявление шаблона, и функцию main.
Реализацию дружественной функции можно пока не указывать.

Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #4 : Октябрь 29, 2013, 22:58 »

Код:
template <class T>
MyVector {          // <---- ошибка синтаксиса: непонятный символ: MyVector
/*...*/
class iterator: // <---- ошибка синтаксиса: похоже на объявление класса, за которым должно последовать наследование.
public:                       
  template <class C>
  friend const iterator& operator+(const iterator& iter, size_type step);
};
};

template <class T>
const typename AtdVector<T>::iterator& operator+(const typename AtdVector<T>::iterator& iter, <---- ошибка синтаксиса: неопознанный символ  AtdVector
                                                 typename AtdVector<T>::size_type step) {
    /*...*/
}



Для начала представьте полностью компилирующийся код, который содержит объявление шаблона, и функцию main.
Реализацию дружественной функции можно пока не указывать.



Ну уж можно уж сообразить то:

1)  MyVector {          // <---- ошибка синтаксиса: непонятный символ: MyVector

Очевидно, class MyVector

2) class iterator: // <---- ошибка синтаксиса: похоже на объявление класса, за которым должно последовать наследование.

Скорее всего наследование от std::iterator, но это не принципиально..

3) AtdVector<T>::iterator& iter, <---- ошибка синтаксиса: неопознанный символ  AtdVector

Несложно догадаться, что по сути это и есть MyVector.. опечатка..



А по сути, ошибка у ТС связана с тем, что в данном случае необходимо опережающее объявление шаблона функции..


Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #5 : Октябрь 29, 2013, 23:36 »

Ну уж можно уж сообразить то:

Это не код, а одна сплошная опечатка.

Я не телепат. Компилятор тоже не телепат.
Записан
schmidt
Гость
« Ответ #6 : Октябрь 31, 2013, 06:34 »

Не принципиально, код не был компилируемым, я об этом заранее предупредил Подмигивающий

Выбросил все несущественные моменты из кода, код можете посмотреть в прикрепленных файлах.

Существенные моменты здесь таковы:
1. Чтобы определить перегруженный бинарный оператор с двумя аргументами он должен быть объявлен вне класса
2. Чтобы определить бинарный оператор для объектов шаблонного класса само собой он тоже должен быть как-то связан с параметром шаблона
3. Если в коде объявлена шаблонная функция, вызов ее принято писать как
Код:
templFunc<int>(...)
то есть с указанием параметра шаблона в момент ее вызова. Вполне возможно, что компилятор "Can't deduce template parameter" именно потому, что в оператор a+b синтаксически невозможно запихнуть параметр шаблона функции.

Значит нужно либо как-то заставить компилятор создавать экземпляр оператора наряду с экземпляром шаблонного класса, либо... стрелять в разработчиков C++ шаблонов за то, что они сидят и в ус не дуют Смеющийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Октябрь 31, 2013, 11:10 »

Код:
typedef unsigned int size_type;
Зачем? Чем плохо незатейливое size_t, тем более сейчас данные > 4Gb вполне возможны

Также неясно к чему вся эта возня с "итератором", чем не устраивало простое T * ? Ведь контейнер явно линейный. Также не вижу кто/где заряжает _INIT_VECTOR_SIZE (да и не очень удачное имя для члена).

В общем, как всегда, силенка ушла в "наведение песиков", а содержательная часть в пролете  Улыбающийся
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #8 : Октябрь 31, 2013, 13:11 »

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

Опять крайности.. ТС просто хочет разобраться с дружественными шаблонными операторами и этот пример нужно воспринимать просто как некий полигон ни больше ни меньше..

Цитировать
Значит нужно либо как-то заставить компилятор создавать экземпляр оператора наряду с экземпляром шаблонного класса, либо... стрелять в разработчиков C++ шаблонов за то, что они сидят и в ус не дуют  Смеющийся

Вначале лучше разобраться в более простом примере: дружественный оператор в шаблонном классе:

Код
C++ (Qt)
#include <iostream>
 
template <class T>
class Iterator;
 
template<class T>
const Iterator<T> operator+(const Iterator<T>&, typename Iterator<T>::size_type);
 
 
template <class T>
class Iterator
{
public:
   typedef size_t size_type;
 
   Iterator() {}
   explicit Iterator(const Iterator &, size_type) {}
 
   friend const Iterator operator+<>(const Iterator &, size_type);
 
private:
 
};
 
template <class T>
const Iterator<T> operator+(const Iterator<T> & it, typename Iterator<T>::size_type n) {
   return Iterator<T>(it, n);
}
 
int main()
{
   Iterator<int> it;
   it + 2;
 
   return 0;
}
 

Обратите внимание на опережающее объявление class Iterator  и дружественного оператора..
 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
schmidt
Гость
« Ответ #9 : Октябрь 31, 2013, 13:20 »

С итератором ли, без, вопрос с перегрузкой бинарных операторов для шаблонных классов все равно актуален ) Хочу докопаться до сути и "отложить на полку, чтобы знать где достать", вместо того, чтобы потом судорожно и в матах копать этот вопрос, когда он будет необходим Улыбающийся

В книге "C++ Templates: The Complete Guide" в разделе Friend Functions нашел информацию о том, что как только шаблонная функция объявлена как friend, ее экземпляр автоматически будет создан компилятором наряду с экземпляром шаблонного класса.

Цитировать
An instance of a function template can be made a friend by making sure the name of the friend function is followed by
angle brackets. The angle brackets can contain the template arguments, but if the arguments can be deduced, the
angle brackets can be left empty

То есть теоретически объявления
Код:
template <class T>
class TplClass;

template <class T>
const TplClass<T>& operator+(const TplClass<T>& lhs, const TplClass<T>& rhs);

template <class T>
class TplClass {
public:
/*...*/
friend const TplClass& operator+(const TplClass<T>& lhs, const TplClass<T>& rhs);
/*...*/
}

должно хватить, чтобы обеспечить ВСЕ экземпляры шаблонного класса оператором сложения. Однако на деле все оказывается совершенно вопреки написанному (выбросил уже ВСЕ что можно, оставив голый оператор сложения):
Код:
#ifndef TPLNUMBER_H
#define TPLNUMBER_H

template <class T>
class TplNumber;

template <class T>
const TplNumber<T>& operator+(const TplNumber<T>& lhs, const TplNumber<T>& rhs);

template <class T>
class TplNumber {
public:
    TplNumber(T val)
        : _val(val)
    {}


    friend const TplNumber& operator+(const TplNumber& lhs, const TplNumber& rhs);

private:
    T _val;
};
///////////////////////////////////////////////////////////////////////////////

template <class T>
const TplNumber<T>& operator+(const TplNumber<T>& lhs, const TplNumber<T>& rhs) {
    return TplNumber<T>(lhs._val + rhs._val);
}

#endif // TPLNUMBER_H

Код:
/* main.cpp */

#include <iostream>

#include "tplnumber.h"

using namespace std;

int main()
{
    cout << "Hello World!" << endl;

    TplNumber<int> num1(4);
    TplNumber<int> num2(3);
    TplNumber<int> sum = num1 + num2;

    return 0;
}

При попытке компиляции:
Цитировать
/main.cpp:13: undefined reference to `operator+(TplNumber<int> const&, TplNumber<int> const&)'

Какие еще могут быть извращенные варианты - ума не приложу... Улыбающийся
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #10 : Октябрь 31, 2013, 13:33 »

Цитировать
Какие еще могут быть извращенные варианты - ума не приложу...  Улыбающийся

Вы как то не внимательно читаете чужие посты(

У меня всё работает (найдите 10 отличий):

Код
C++ (Qt)
template <class T>
class TplNumber;
 
template <class T>
const TplNumber<T> operator+(const TplNumber<T>&, const TplNumber<T>&);
 
template <class T>
class TplNumber {
public:
   TplNumber(T val)
       : _val(val)
   {}
 
 
   friend const TplNumber operator+<>(const TplNumber &, const TplNumber &);
 
private:
   T _val;
};
 
 
template <class T>
const TplNumber<T> operator+(const TplNumber<T>& lhs, const TplNumber<T>& rhs) {
   return TplNumber<T>(lhs._val + rhs._val);
}
 
 
int main()
{
   TplNumber<int> num1(4);
   TplNumber<int> num2(3);
   TplNumber<int> sum = num1 + num2;
 
   return 0;
}
 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
schmidt
Гость
« Ответ #11 : Октябрь 31, 2013, 14:15 »

Цитировать
Вы как то не внимательно читаете чужие посты(

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

Ммм, насчет 10 не уверен, но вот на этом месте
Код:
friend const TplNumber& operator+<>(const TplNumber& lhs, const TplNumber& rhs);
Qt Creator выделяет красным и пишет " unexpected token '<' ". Это меня сбило с толку, когда я похожий пример пробовал скопировать с интернета в код.... Хотя, оказалось, строка компилируется... Шокированный Благодарю за помощь Улыбающийся Видать Qt Creator плохо дружит с операторами и шаблонами?
Записан
schmidt
Гость
« Ответ #12 : Ноябрь 04, 2013, 11:21 »

*Убиться об стену*

А вот здесь http://www.parashift.com/c++-faq-lite/template-friends.html нашел, что, оказывается, все легко и просто В замешательстве :

Код:
template <class T>
class TplNumber {
// ...
friend const TplNumber& operator+<>(const TplNumber& lhs, const TplNumber& rhs) {
    return TplNumber(lhs.value() + rhs.value());
}
// ...
};

код френда объявляется прямо в классе, что делает все пляски с бубном абсолютно бессмысленными...

//--------------------------------------------------------------------------------------------------------
P.S. Сделал вывод, что разносить объявление и определение шаблонных классов - ужасная и крайне вредная затея... Смеющийся
« Последнее редактирование: Ноябрь 04, 2013, 11:24 от Schmidt » Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.096 секунд. Запросов: 22.