Вопрос по шаблонам
{ public:
class B { };
};
template <class T> bool operator ==(A<T>::B, A<T>::B) { return 1; }
int main()
{
A<int>::B a, b;
a == b;
}
Почему этот код при компиляции выдает вышеуказанную ошибку, вроде бы оператор объявлен правильно для данного случая?
Я создаю шаблонный класс, в нем еще один класс (не шаблоннный), однако при попытке объявить оператор для второго класса, BC++B6 пишет, что 'operator==' not implemented in type 'A<int>::B' for arguments of same type
{ public:
class B { };
};
template <class T> bool operator ==(A<T>::B, A<T>::B) { return 1; }
int main()
{
A<int>::B a, b;
a == b;
}
Почему этот код при компиляции выдает вышеуказанную ошибку, вроде бы оператор объявлен правильно для данного случая?
Круто. А что за операция
где объявление == в классе?
Можно же объявить
Если написать
В принципе, объявление внутри класса допустимо, но хочется понять, что в моем варианте неправильно.
В принципе, объявление внутри класса допустимо, но хочется понять, что в моем варианте неправильно.
Все у тебя правильно.
У меня все нормально компилируется (VC++ 7.0) и выполняется. Думаю, это баг компилятора.
P.S. Ща буду занудствовать: если у тебя возвращаемое значение объявлено, как bool, то и возвращай bool (true, false), а не целочисленное значение (1).
В этом-то и дело, что хотелось объявить вне класса.
Можно же объявить
Если написать
В принципе, объявление внутри класса допустимо, но хочется понять, что в моем варианте неправильно.
Вопросы чистой религии.
Мне кажется потому, что ищет шаблон для A<int>::B, а у тебя все таки для int.
В этом-то и дело, что хотелось объявить вне класса.
Можно же объявить
Если написать
В принципе, объявление внутри класса допустимо, но хочется понять, что в моем варианте неправильно.
Все у тебя правильно.
У меня все нормально компилируется (VC++ 7.0) и выполняется. Думаю, это баг компилятора.
мне кажется, что надо так
template <class T> bool operator ==(T, T ) { return 1; }
Выглядит правильно, и борланд заткнулся
мне кажется, что надо так
template <class T> bool operator ==(T, T ) { return 1; }
Выглядит правильно, и борланд заткнулся
:)
Тогда это будет работать для всех классов (типов), а не только для A<T>::B.
Т.е.
{
public:
C(int val) :val(val) {}
int val;
};
C(1) == C(2); // Тоже вернет true !!!
template <class T> T operator ==(A<T>::B a, A<T>::B b){return 1;}
Оператор должен вернуть bool, а не T
krl, настаиваю на том, что это ошибка компилятора.
Кстати, я бы использовал передачу аргументов по ссылке, т.е.
template <class T>
bool operator ==(A<T>::B&, A<T>::B&)
{ return true; }
:)
Тогда это будет работать для всех классов (типов), а не только для A<T>::B.
Конечно. Но я думаю, что мы чисто теоретический код расматриваем, т.к. a == b тоже смысла не имеет. Я просто думаю, что в данном случае компилятор хочет, чтобы было именно так
template <class T> bool operator ==(T, T ) { return 1; }
и ни как по другому. Так что надо подход менять. Ну или компилятор :)
Кстати, я бы использовал передачу аргументов по ссылке
Понятно, что лучше по ссылкам, просто от этого ничего здесь не меняется, проблема все равно остается.
Так что надо подход менять. Ну или компилятор
Так вот и непонятно, на что менять подход. Работать-то хоть как-то надо. Опять же, можно засунуть оператор и в сам класс, но IMHO это неэлегантно и неправильно (т.к. по смыслу задачи оператору доступ к самим элементам класса не обязателен, даже в std::vector операторы сравнения вынесены из самого класса).
С компилятором понятнее, но сложнее (намного).
Вроде как нашел один способ:
template <class T> class A
{ public:
typedef C<T> B;
};
template <class T> bool operator ==(C<T>, C<T>) { return 1; }
int main()
{
A<int>::B a, b;
a == b;
}
P.S. Идея пришла от просмотров исходников std::list, там тоже iterator объявлен через typedef. Раз уж там так сделано, наверное, это единственный вариант.
:)
Тогда это будет работать для всех классов (типов), а не только для A<T>::B.
Т.е.
{
public:
C(int val) :val(val) {}
int val;
};
C(1) == C(2); // Тоже вернет true !!!
Оператор должен вернуть bool, а не T
krl, настаиваю на том, что это ошибка компилятора.
Кстати, я бы использовал передачу аргументов по ссылке, т.е.
template <class T>
bool operator ==(A<T>::B&, A<T>::B&)
{ return true; }
Компилятор здесь не причем. Ошибка, и моя в том числе в том, что при обявлении шаблона нужно использовать:
на то это и шаблон.:)
То что компилятор мелкомягких пытается править ошибки программиста - к С++ и шаблонам никакого отношения не имеет.
Тут вопрос уже в другом - на сколько рационально и необходимо определять шаблон для проверки на равенство объектов. Не проще ли определить его в классе? Кроме распухания кода польза сомнительная.
Тут вопрос уже в другом - на сколько рационально и необходимо определять шаблон для проверки на равенство объектов. Не проще ли определить его в классе? Кроме распухания кода польза сомнительная.
[6]Prefer member functions over nonmembers for operations that need access to the representation.
[7]Prefer nonmember functions over members for operations that do not need access to the representation
11.5.2
if implicit type conversion is desired for all operands of an operation, the function implementing it must be a nonmember function taking a const reference or a non-reference argument.
По-моему это как раз относится к моему случаю.
Компилятор здесь не причем. Ошибка, и моя в том числе в том, что при обявлении шаблона нужно использовать:
на то это и шаблон.:)
Неа... Ты не прав.
В коде действительно одна ошибка, которую VC++7.0 пытается править, а VC++7.1 уже сигнализирует. И ошибка эта в отсутствии typename:
template <class T>
bool operator ==(typename A<T>::B, typename A<T>::B);
В коде действительно одна ошибка, которую VC++7.0 пытается править, а VC++7.1 уже сигнализирует. И ошибка эта в отсутствии typename:
template <class T>
bool operator ==(typename A<T>::B, typename A<T>::B);
Даже если добавить typename, все равно не работает
Даже если добавить typename, все равно не работает
typename = class
По стандарту рекомендуется использовать первое - исходя из того что в шаблон могут загружатся типы данных, классами не являющиеся. Но это не ошибка, по меньшей мере пока. Допустимо использовать и typename и class.Другое дело что список параметров должен соответствовать списку параметров шаблона. То есть ошибка объявлять шаблон вот так:
В качестве параметра шаблона мы передаем Т а в список параметров функции предаем что? В таком случае мы должны явно инстанцировать шаблон для ...для...для чего?...:)
Скорее всего VC в данном случае просто инстанцирует это ввиде
template <typename T::B> bool operator ==(typename T::B a, typename T::B b){return 1;}
не особо заботясь, что на самом деле имел ввиду разработчик. А что имел он ввиду он помоему и сам не догадывается...:)
Ну а если серьезно, то фактически вырождается все это в ту же форму template<typename T>bool operator==(T,T).
Все это пока теоретически - будет время проверьте.
typename = class
По стандарту рекомендуется использовать первое - исходя из того что в шаблон могут загружатся типы данных, классами не являющиеся.
Это ты вообще не из той оперы взял.
Другое дело что список параметров должен соответствовать списку параметров шаблона.
А вот это уже полная ерунда.
Смотрим стандарт!
То есть ошибка объявлять шаблон вот так:
В качестве параметра шаблона мы передаем Т а в список параметров функции предаем что? В таком случае мы должны явно инстанцировать шаблон для ...для...для чего?...:)
Скорее всего VC в данном случае просто инстанцирует это ввиде
template <typename T::B> bool operator ==(typename T::B a, typename T::B b){return 1;}
не особо заботясь, что на самом деле имел ввиду разработчик. А что имел он ввиду он помоему и сам не догадывается...:)
Ну а если серьезно, то фактически вырождается все это в ту же форму template<typename T>bool operator==(T,T).
А это уже скороспелые неверные выводы и обилие эмоций.
Это ты вообще не из той оперы взял.
А вот это уже полная ерунда.
Смотрим стандарт!
А это уже скороспелые неверные выводы и обилие эмоций.
Ну смотрим стандарт:
1 Given two function templates, whether one is more specialized than
another can be determined by transforming each template in turn and
using argument deduction (_temp.deduct_) to compare it to the other.
2 The transformation used is:
--For each type template parameter, synthesize a unique type and sub-
stitute that for each occurrence of that parameter in the function
parameter list.
--For each non-type template parameter, synthesize a unique value of
the appropriate type and substitute that for each occurrence of that
parameter in the function parameter list.
3 Using the transformed function parameter list, perform argument deduc-
tion against the other function template. The transformed template is
at least as specialized as the other if, and only if, the deduction
succeeds and the deduced parameter types are an exact match (so the
deduction does not rely on implicit conversions).
4 A template is more specialized than another if, and only if, it is at
least as specialized as the other template and that template is not at
least as specialized as the first. [Example:
template<class T> struct A { A(); };
template<class T> void f(T);
template<class T> void f(T*);
template<class T> void f(const T*);
template<class T> void g(T);
template<class T> void g(T&);
template<class T> void h(const T&);
template<class T> void h(A<T>&);
В чем мои выводы скороспелы, неверны и наполнены эмоциями? Может с точки зрения теории они и не идеальны, но:
"Существенным моментом здесь является возможность выведения (deduction)типа аргументов шаблона функции по типам аргументов при ее вызове.
Компилятор может вывести аргументы, как являющиеся типами, так и обычные, при условии, что список аргументов функции однозначно индентифицирует набор аргументов цаблона..."
третья редакция стр.385.
и твоим:
Однозначно идентифицирует - это не тоже самое, что соответствует (в твоем понимании "совпадает").
Для наглядности:
template<class T> void func(A<T>); // однозначно идентифицирует
template<class T> void func(A<T>::B); // однозначно идентифицирует
template<class T> void func(T); // совпадает
Так где в стандарте сказано, что тип аргумента должен совпадать с параметром шаблона?
Я не понимаю всего этого флейма.
Я привожу пример из стандарта:
template<class T> void sort(Array<T>&);
Ты сам приводишь пример из стандарта:
template<class T> void h(A<T>&);
И при этом утверждаешь, что это неправильно?
Что тип аргумента функции должен быть точно таким же как параметр шаблона.
Получается что и STL, и Boost, и Loki не должны работать?
настаиваю на том, что это ошибка компилятора.
Я утверждаю что форма записи:
template<class T> void func(A<T>::B);
не верна и ошибки компилятора здесь ни причем.
Параметр типа шаблона определен один, в параметры функции передается другой тип.
Не одно и тоже -
template<class T> void func(A<T>::B&);
и
template<class T> void func(A<T>&);
Если бы можно было однозначно идентифицировать при этом тип объекта - не было бы проблем.
Или второй вариант - явно инстанцировать шаблон для конкретного типа данных.
К сожалению, у меня нет возможности сейчас проверить как и что будет работать, поэтому в отличии от тебя я не утверждаю что мои слова - истина в последней инстанции, и не ищу ошибки компиляторов.
Кстати, по той же причине не могу покопаться в STL - в каких шаблонах функций используется ТАКОЕ определение? Я не помню. Но я могу и ошибаться.
Я утверждаю что форма записи:
template<class T> void func(A<T>::B);
не верна и ошибки компилятора здесь ни причем.
Параметр типа шаблона определен один, в параметры функции передается другой тип.
Не одно и тоже -
template<class T> void func(A<T>::B&);
и
template<class T> void func(A<T>&);
К сожалению, у меня нет возможности сейчас проверить как и что будет работать, поэтому в отличии от тебя я не утверждаю что мои слова - истина в последней инстанции, и не ищу ошибки компиляторов.
Кстати, по той же причине не могу покопаться в STL - в каких шаблонах функций используется ТАКОЕ определение? Я не помню. Но я могу и ошибаться.
Ну я тоже ничего не констатирую, просто стараюсь отстоять свою точку зрения.
Кстати, она несколько расшаталась... :D
Пункт 14.8.2.10 внес сомнения в мою позицию:
T deduce(A<T>::X x, // T is not deduced here
T t, // but T is deduced here
B::Y y); // i is not deduced here
Кстати, пока мы спорили, никто не догадался попробовать:
template<class T> void func(A<T>::B*);?
Ведь в таком случае тип определен, проблемы конструирования объекта становятся задачей программиста и компилятор спокойно это съест. Проблемы будут только если функция возвращает значение (я имею ввиду здесь только проблемы компиляции, остальные не трогаем)Или я ошибаюсь?
Наверное, проще всего объявить тип через typedef внутри класса, и сам класс описать отдельно.
Не понимаю, какие проблемы могут быть, если такая функция возвращает значение? И неужели удобно так работать с указателями?
Кстати, у Страуструпа написано, какие правила ипользуются при выведении типов параметров функций (C.13.4) Есть только class_template_name<T>, class_template_name, где T - type, I - non-type. Варианта class_template_name<T>::* там нет.
Наверное, проще всего объявить тип через typedef внутри класса, и сам класс описать отдельно.
Не понимаю, какие проблемы могут быть, если такая функция возвращает значение? И неужели удобно так работать с указателями?
Ну не не знаю, думаю в целом ряде библиотек ты сможешь найти конструкции типа:
использование параметров, не являющихся типами - т.е. - целочисленный, тип указателя и ссылочный - допустимо. Если для тебя уж так значимо использование шаблонной функции для проверки полей класса - стоит попробовать.По поводу проблем с возвращаемым значением - честно не помню...:)
Использование указателей - рекомендуется если твоя система не критична по пямяти и быстродействию. Так как при использовании указателей происходит выделение памяти из кучи, т.е. в конкретном месте программы размер объекта может быть не известен, для системы в которой эта память мала, выделение может закончится крахом. Разыменовывание указателя -> так же может быть операцией с высокими накладными расходами. С другой стороны - использование указателей может сделать программу гораздо более эффективной - особенно если размер кучи достаточно велик. Все завистит от того какие задачи перед тобой стоят. Но практически со сто процентной уверенностью можно сказать - там где используются шаблоны, имеет смысл использовать указатели. С умом.:D :D
Ну не не знаю, думаю в целом ряде библиотек ты сможешь найти конструкции типа:
использование параметров, не являющихся типами - т.е. - целочисленный, тип указателя и ссылочный - допустимо.
Посмотрел имеющиеся библиотеки (STL, STLPort, Loki, Boost-а к сожалению на этой машине нет). Нигде не нашел такой конструкции, за исключением методов-членов, но они не в счет.
Можешь показать места, где такое встречается. Может, я плохо искал... :о\
Попробовал откомпилировать пример с использованием указателей, нескомпилировалось.
Видимо, все же нельзя и указатели использовать.
Кроме того они не перечисленны в случиях дедукции параметров.
Кстати, ссылка на Comeau онлайн компилятор, утверждают, что полностью поддерживает стандарт:
http://www.comeaucomputing.com/tryitout/
1. Ключевое слово typename
"Это ключевое слово введено в язык в процессе стандартизации С++ для указания того, что идентификатор в шаблоне является типом. Рассмотри следующий пример:
typename T::SubType* ptr;
В этом примере второе ключевое слово typename используется для пояснения, что SubType является типом, определенным внутри класса Т. Таким образом, ptr является указателем на T::SubType. Без такого указания с помощью typename идентификатор SubType интерпретировался как статический член класса, т.е. как конкретная переменная или объект. В результате выражение
T::SubType *ptr представляло бы умножение статического члена класса SubType на объект ptr..."(Вандервурд Д. "Шаблоны С++. Справочник разработчика", гл. 5.1 )
2. По поводу использования в шаблонах функций указателей на члены классов-шаблонов - мое предположение было неверным. По крайней мере ни где в стандартной библиотеке я не нашел что либо похожего, при попытке создать что либо таким образом - компилятор выдает ошибку. Как говорится - не в сказке живем...:)