Russian Qt Forum

Программирование => С/C++ => Тема начата: schmidt от Октябрь 29, 2013, 00:36



Название: Перегрузка бинарных операторов для шаблонного класса
Отправлено: 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 и ничего лишнего"?


Название: Re: Перегрузка бинарных операторов для шаблонного класса
Отправлено: Igors от Октябрь 29, 2013, 09:41
Код:
  template <class C>
  friend const iterator& operator+(const iterator& iter, size_type step);
Мои компиляторы такого не пропускают - нет аргуметов зависящих от "C"

Код:
typename AtdVector<T>::size_type step
Вот после таких экзерцисов утверждение "С++ монструозный язык" не кажется столь уж абсурдным  :'(  А если это еще воспринимается как бооольшая вумность .....


Название: Re: Перегрузка бинарных операторов для шаблонного класса
Отправлено: mutineer от Октябрь 29, 2013, 10:08
Цитировать
undefined reference to `operator+(AtdVector<int>::iterator const&, unsigned int)'

А почему компилятор считает второй параметр интом?


Название: Re: Перегрузка бинарных операторов для шаблонного класса
Отправлено: _Bers от Октябрь 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.
Реализацию дружественной функции можно пока не указывать.



Название: Re: Перегрузка бинарных операторов для шаблонного класса
Отправлено: m_ax от Октябрь 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.. опечатка..



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




Название: Re: Перегрузка бинарных операторов для шаблонного класса
Отправлено: _Bers от Октябрь 29, 2013, 23:36
Ну уж можно уж сообразить то:

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

Я не телепат. Компилятор тоже не телепат.


Название: Re: Перегрузка бинарных операторов для шаблонного класса
Отправлено: schmidt от Октябрь 31, 2013, 06:34
Не принципиально, код не был компилируемым, я об этом заранее предупредил ;)

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

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

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


Название: Re: Перегрузка бинарных операторов для шаблонного класса
Отправлено: Igors от Октябрь 31, 2013, 11:10
Код:
typedef unsigned int size_type;
Зачем? Чем плохо незатейливое size_t, тем более сейчас данные > 4Gb вполне возможны

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

В общем, как всегда, силенка ушла в "наведение песиков", а содержательная часть в пролете  :)


Название: Re: Перегрузка бинарных операторов для шаблонного класса
Отправлено: m_ax от Октябрь 31, 2013, 13:11
Цитировать
В общем, как всегда, силенка ушла в "наведение песиков", а содержательная часть в пролете   :)

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

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

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

Код
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  и дружественного оператора..
 


Название: Re: Перегрузка бинарных операторов для шаблонного класса
Отправлено: schmidt от Октябрь 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&)'

Какие еще могут быть извращенные варианты - ума не приложу... :)


Название: Re: Перегрузка бинарных операторов для шаблонного класса
Отправлено: m_ax от Октябрь 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;
}
 


Название: Re: Перегрузка бинарных операторов для шаблонного класса
Отправлено: schmidt от Октябрь 31, 2013, 14:15
Цитировать
Вы как то не внимательно читаете чужие посты(

 ::) долго писал, видать, отправил, не увидел ваш пост  :)

Ммм, насчет 10 не уверен, но вот на этом месте
Код:
friend const TplNumber& operator+<>(const TplNumber& lhs, const TplNumber& rhs);
Qt Creator выделяет красным и пишет " unexpected token '<' ". Это меня сбило с толку, когда я похожий пример пробовал скопировать с интернета в код.... Хотя, оказалось, строка компилируется... :o Благодарю за помощь :) Видать Qt Creator плохо дружит с операторами и шаблонами?


Название: Re: Перегрузка бинарных операторов для шаблонного класса
Отправлено: schmidt от Ноябрь 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. Сделал вывод, что разносить объявление и определение шаблонных классов - ужасная и крайне вредная затея... ;D