[c++] о специализации шаблонов
#include <iostream>
using namespace std;
template<class T>
class A {
public:
void f;
void g {
cout << "g" << endl;
}
};
template<class T>
void A<T>::f {
cout << "f" << endl;
}
template<>
void A<int>::f {
cout << "f int" << endl;
}
main {
A<int> a;
A<double> a2;
a.f;
a.g;
a2.f;
a2.g;
}
спасибо большое!
![](/images/graemlins/cool.gif)
#include <iostream>
using namespace std;
template<class T, class S>
class A {
public:
void f;
void g {
cout << "g" << endl;
}
};
template<class T, class S>
void A<T,S>::f {
cout << "f" << endl;
}
template<class S>
void A<int,S>::f {
cout << "f int" << endl;
}
main {
A<int,void> a;
A<double,void> a2;
a.f;
a.g;
a2.f;
a2.g;
}
$ g++ -V 3.3.6 test.cpp
test.cpp:18: error: no `void A<int, S>::f' member function declared in class
`A<int, S>'
test.cpp:18: error: template definition of non-template `void A<int, S>::f'
Здесь придется делать частичную специализацию класса A, при этом не избежать копирования всех его методов.
кстати, утром у меня появилась идея, как можно избежать копирования с помощью наследования:
#include <iostream>
using namespace std;
namespace detail {
template<class T, class S>
class A_help {
public:
void f;
void g {
cout << "g" << endl;
}
};
template<class T, class S>
void A_help<T,S>::f {
cout << "f" << endl;
}
} // detail
template<class T, class S>
class A : public detail::A_help<T,S>
{
};
template<class S>
class A<int,S> : public detail::A_help<int,S>
{
public:
void f;
};
template<class S>
void A<int,S>::f {
cout << "f int" << endl;
}
main {
A<int,void> a;
A<double,void> a2;
a.f;
a.g;
a2.f;
a2.g;
}
Если не ошибаюсь, для функций и методов возможна только полная специализация.ошибаешься
Только вот не надо путать шаблон функции с методом шаблона класса. Это как бы понятия совсем разные.
Только вот не надо путать шаблон функции с методом шаблона класса. Это как бы понятия совсем разныеНе совсем тебя понял.
Метод шаблона класса - сам по себе шаблон, и его можно специализировать отдельно от класса. Именно это и делалось в первом примере. И в этом смысле он очень похож на шаблон функции.
ошибаешься
Можешь привести пример частичной специализации функции, или метода (шаблонного) класса ?
Метод шаблона класса - сам по себе шаблон, и его можно специализировать отдельно от класса.Вот в этом и состоит основное заблуждение.
Метод шаблонного класса не есть шаблон (если не объявлен как шаблон внутри класса) с точки зрения логики C++, хотя конечно в известном смысле он подобен шаблону, но у него не может быть специализации - специализация у класса, поскольку шаблоном является класс.
Например A<double>::f не есть специализация A<T>::f, но A<double> является специализацией A<T>.
И попытка определить A<double>::f, подразумевает, что где-то определена специализация A<double> и f объявлена ее методом. Автоматической неявной специализации класса не происходит, если просто делается попытка объявить метод специализации класса.
Можешь привести пример частичной специализации функции, или метода (шаблонного) класса ?Конечно:
class A {
double f;
public:
template<class T,class S>
S scale(T x,S y) { return f*x*y; }
template<class S>
S scale<double,S>(double x,S y) { return f*x*y; }
};
В этом примере класс A конечно может быть и шаблонным (или его может вообще не быть, тогда это будет шаблон обычной функции, а не метода).
Разумеется этот код не то, что ты имел в виду и ожидал увидеть, но это именно то что ты фактически попросил, т.к. специализация в принципе может быть только у шаблона, то я и привел пример специализации шаблона.
Казалось бы,
template<>- как раз объявление метода специализации класса.
void A<int>::f
Почитав стандарт я понял, что без поллитры не разберешься в логике template-ов (и вообще Страуструп похоже был сильно накурен или пьян, когда придумывал их синтаксис и правила).
PS: А вот чего тот код, который во втором посту, работает, и правда непонятно - там функция main объявлена синтаксически неверно
![](/images/graemlins/smile.gif)
(hint: у всякой функции должен быть указан тип возврата и если он не void, то внутри должен происходить возврат значения соответствующего типа).
PS: А вот чего тот код, который в первом посту работает, и правда непонятно - там функция main объявлена синтаксически невернопотому что компилятор умный
если очень хочется, можно предупреждения включить, он скажет
также всякие опции соответствия стандартам есть...
потому что компилятор умныйимхо наоборот, потому что компилятор глупый (или очень старый)
это ж помарки..
что ж плохого в том, что он угадывает то, что программист хотел написать?очевидно плохо то, что программист при этом не учится и не привыкает писать правильно.
Не говоря уже о том, что ничего компилятор в данном случае не угадывает, поскольку программист (новичок как правило) не написав тип возврата, обычно думает, что возвращать ничего не надо, а на самом деле возврат по умолчанию (K&R C) - тип int (у функции main кроме того обязан быть тип возврата int). А отсутствие самого возврата означает возврат мусора (который завалялся в регистре возврата (EAX обычно для ia32 архитектуры. Это все очень далеко от того, чтобы "угадывать мысли программиста".
понятное дело, что стандарт согласовывали мыслящие люди, он - вещь очень и очень полезная, но не нужно на него молиться
к тому же, как я уже написал, можно легко решить такую "проблему" с помощью пары свичей
А отсутствие самого возврата означает возврат мусора (который завалялся в регистре возврата (EAX обычно для ia32 архитектуры. Это все очень далеко от того, чтобы "угадывать мысли программиста".
неужели ты и правда так думаешь?
по-твоему, создатели компилера не смогли ничего лучше придумать, чем сваливать туда мусор?
неужели ты и правда так думаешь?я не думаю, я это знаю совершенно точно (по поводу большинства компиляторов)
по-твоему, создатели компилера не смогли ничего лучше придумать, чем сваливать туда мусор?
это довольно легко проверить - скомпилить в ассемблерный код (в MSVC с листингом)
компилер просто ничего не делает, если ты не написал возврат (никакой "мусор" он специально не "сваливает" туда, просто текущее значение возврата ничем специально не заполняется, что там было раньше, то и осталось после возврата).
Откуда компилятор может знать какой ты извращенец, может ты ассемблерной вставкой какой-нибудь заполнил регистр возврата нужным тебе значением, например. Еще у многих компиляторов значение последнего вычисленного выражения находится в области возврата.
В качестве "мусора" как я уже сказал выступает текущее значение регистра возврата (или вершина стека, если в регистр не помещается, или еще чего-нибудь зависящее от платформы)
то, что "правильно" - вещь субъективнаянет, какая программа на C++ правильная - это вещь не субъективная, а прописанная в стандарте C++ совершенно четко и однозначно (точнее там парочка других терминов вводится вместо "правильная программа").
Это как заявить: "сколько будет 2+2 - вещь субъективная".
C++ конечно в процессе разработки, но есть в нем совершенно четко устоявшиеся вещи и необходимость писать тип возврата при объявлении любой функции - одна из них.
понятное дело, что стандарт согласовывали мыслящие люди, он - вещь очень и очень полезная, но не нужно на него молитьсяникто и не молится
Я лично стандарт C++ воспринимаю тем, чем он должен по идее являться - определением языка C++ (и не беда, что он до сих пор не реализован в наиболее популярных компиляторах,
![](/images/graemlins/smile.gif)
(А почитав про шаблоны я еще меньше стал уважать стандарт C++ чем раньше, хотя и раньше я был далеко не в восторге от него)
к тому же, как я уже написал, можно легко решить такую "проблему" с помощью пары свичейэээ
![](/images/graemlins/confused.gif)
какую проблему? каких свичей?
проблему с возвратом функции main?
![](/images/graemlins/smile.gif)
компилер просто ничего не делает, если ты не написал возврат (никакой "мусор" он специально не "сваливает" туда, просто текущее значение возврата ничем специально не заполняется, что там было раньше, то и осталось после возврата).
Откуда компилятор может знать какой ты извращенец, может ты ассемблерной вставкой какой-нибудь заполнил регистр возврата нужным тебе значением, например. Еще у многих компиляторов значение последнего вычисленного выражения находится в области возврата.
В качестве "мусора" как я уже сказал выступает текущее значение регистра возврата (или вершина стека, если в регистр не помещается, или еще чего-нибудь зависящее от платформы)
я тебя понял, просто про "сваливает" некорректно выразился, признаю
вообще, с написанным согласен
нет, какая программа на C++ правильная - это вещь не субъективная, а прописанная в стандарте C++ совершенно четко и однозначно (точнее там парочка других терминов вводится вместо "правильная программа").
Это как заявить, что "сколько будет 2+2 - вещь понимаете ли субъективная".
в том контексте я понял "правильно" не в смысле "согласно стандарту"
Я лично стандарт C++ воспринимаю тем, чем он должен по идее являться - определением языка C++ (и не беда, что он до сих пор не реализован в наиболее популярных компиляторах, зато они к этому стремятся в меру своих убогих возможностей). Что же еще считать определением, если не стандарт? Тем более это касается тех частей стандарта, которые всеми реализованы и притом одинаково.
вообще, у популярных компилеров есть куча своих расширений. можешь считать отсутствие требования int у main таким расширением
эээ
какую проблему? каких свичей?
проблему с возвратом функции main?
с помощью свичей (switches) можно включать/отключать свойства компилятора, например решить "проблему" отсутствия строгого следования стандарту
вообще, у популярных компилеров есть куча своих расширений. можешь считать отсутствие требования int у main таким расширениемрасширения бывают грубо говоря полезные, безобидные и вредные. Имхо это "расширение" (точнее рудимент "обратной совместимости" с K&R C) без сомнения вредное - оно не предоставляет никаких дополнительных возможностей, но сбивает с толку новичков (которые могут думать, что пустота значит void) и ухудшает читабельность кода.
но сбивает с толку новичков (которые могут думать, что пустота значит void)не всех, а только тех, кто недочитал книжку
кстати, что такого страшного произойдет, если программа вернет мусор? ну шелл неправильно отреагирует, ну засуетится новичок
![](/images/graemlins/grin.gif)
![](/images/graemlins/cool.gif)
кстати, что такого страшного произойдет, если программа вернет мусор? ну шелл неправильно отреагирует, ну засуетится новичокВ том то и проблема, что не засуетится, поскольку с очень большой вероятностью не заметит. Нет сообщения об ошибке - нет проблем.
Обычно ничего страшного, если это функция main, а если значение другой какой-то функции будет считываться и обрабатываться, но не возвращаться из этой функции?
Короче даже в стандарте C90 такой синтаксис был устаревшим (может даже в C99 запретили наконец, хз а в C++ он был некорректным всегда.
В принципе логично, что если уж пишешь на C++, то и пиши на C++, а не на суржике каком-то. ИМХО.
В том то и проблема, что не засуетится, поскольку с очень большой вероятностью не заметит. Нет сообщения об ошибке - нет проблем.если не заметит - тоже ничего плохого
ведь если ему не нужен код возврата из программы - то зачем ему навязывать?
Обычно ничего страшного, если это функция main, а если значение другой какой-то функции будет считываться и обрабатываться, но не возвращаться из этой функции?эээ, нет! мы говорим о функции main. для другой функции код просто не откомпилируется.
да и вообще, ну укажет он int как возвращаемое значение, но ничего не вернет - с отключенными предупреждениями компилятор ему ничего не скажет - так что проблему мусора это не решает
ISO/IEC 9899:1999 Programming Language C & ISO/IEC 14882:1998 Programming Language C++?
если не заметит - тоже ничего плохогоя же вполне конкретно сказал, что именно плохо, на мой взляд.
Что программист так и не узнает, что у ВСЕХ функций должен быть тип возврата. И что функция main - не какое-то "особое место" в програме, а обычная функция как и все остальные с таким же синтаксисом объявления (но с определенными требованиями к ее типу).
эээ, нет! мы говорим о функции main. для другой функции код просто не откомпилируется.Тем в большей степени это подтверждает утверждение о тупости компилятора. Что за фигня, когда функции main позволено то, что не позволено другим функциям?
кстати, хотел узнать, раз ты разбираешься в стандартах - какие считать текущими?ну очевидно текущими логично считать самые последние утвержденные стандарты.
ISO/IEC 9899:1999 Programming Language C & ISO/IEC 14882:1998 Programming Language C++?насколько я знаю последний стандарт C++ - ISO/IEC 14882:2003
а C вроде бы да, пока C99 (ISO/IEC 9899:1999) последний
вопрос о вредности является глубоко субъективным, поэтому предлагаю закончить флейм на эту тему
![](/images/graemlins/grin.gif)
меня вот какой вопрос занимает: где в стандарте написано, что для функции обязательно описывать возвр. знач-е?
я полистал, трудно с непривычки его читать, вот нашел кое-что:
8.4 Function definitions [dcl.fct.def]не означает ли все это, что я мог не писать 'int' в примере (см. выделение жирным)?
1 Function definitions have the form
function-definition:
decl-specifier-seq_opt declarator ctor-initializer_opt function-body
decl-specifier-seq_opt declarator function-try-block
function-body:
compound-statement
The declarator in a function-definition shall have the form
D1 ( parameter-declaration-clause ) cv-qualifier-seqopt exception-specificationopt
as described in 8.3.5. A function shall be defined only in namespace or class scope.
2 [Example: a simple example of a complete function definition is
int max(int a, int b, int c)
{
int m = (a > b) ? a : b;
return (m > c) ? m : c;
}
Here int is the decl-specifier-seq; max(int a, int b, int c) is the declarator; { /* ... */ } is
the function-body. ]
не должно бы означать, но тогда что это означает?
![](/images/graemlins/crazy.gif)
не означает ли все это, что я мог не писать 'int' в примере (см. выделение жирным)?Может и означать, если из других частей стандарта не следует обратное.
Я бы очень удивился, если бы узнал, что так и есть.
не должно бы означать, но тогда что это означает?Как минимум это означает, что это определение синтаксиса определения всякой функции, в том числе конструктора, деструктора, оператора преобразования типа - а они объявляются без типа возврата.
Означает ли это, что тип возврата можно не писать в обычных функциях, хз. Если так то непонятно, почему компиляторы ругаются, и, насколько я помню, Страуструп заявляет обратное.
Оставить комментарий
a10063
есть шаблон класса test<T>для T == void он не специализируется, поэтому подумываю написать специализацию test<void>
проблема только в одной функции шаблона, и только она будет переписана в test<void>
есть какой-то элегантный способ избежать копирования кода остальных функций?