[c++] о специализации шаблонов

a10063

есть шаблон класса test<T>
для T == void он не специализируется, поэтому подумываю написать специализацию test<void>
проблема только в одной функции шаблона, и только она будет переписана в test<void>
есть какой-то элегантный способ избежать копирования кода остальных функций?

ppplva

Типа такого ?
#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;
}

a10063

точно!
спасибо большое!

a10063

не подскажете, почему сходный код с частичной специализацией не работает?

#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'

ppplva

Если не ошибаюсь, для функций и методов возможна только полная специализация.
Здесь придется делать частичную специализацию класса A, при этом не избежать копирования всех его методов.

a10063

спасибо
кстати, утром у меня появилась идея, как можно избежать копирования с помощью наследования:
#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;
}

mira-bella

Если не ошибаюсь, для функций и методов возможна только полная специализация.
ошибаешься
Только вот не надо путать шаблон функции с методом шаблона класса. Это как бы понятия совсем разные.

ppplva

Только вот не надо путать шаблон функции с методом шаблона класса. Это как бы понятия совсем разные
Не совсем тебя понял.
Метод шаблона класса - сам по себе шаблон, и его можно специализировать отдельно от класса. Именно это и делалось в первом примере. И в этом смысле он очень похож на шаблон функции.
ошибаешься

Можешь привести пример частичной специализации функции, или метода (шаблонного) класса ?

mira-bella

Метод шаблона класса - сам по себе шаблон, и его можно специализировать отдельно от класса.
Вот в этом и состоит основное заблуждение.
Метод шаблонного класса не есть шаблон (если не объявлен как шаблон внутри класса) с точки зрения логики 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 конечно может быть и шаблонным (или его может вообще не быть, тогда это будет шаблон обычной функции, а не метода).
Разумеется этот код не то, что ты имел в виду и ожидал увидеть, но это именно то что ты фактически попросил, т.к. специализация в принципе может быть только у шаблона, то я и привел пример специализации шаблона.

ppplva

Почему тогда первый пример работает ?
Казалось бы,
template<>
void A<int>::f
- как раз объявление метода специализации класса.

mira-bella

Вообще насчет специализаций беру свои слова назад (я хозяин своего слова, гыгы).
Почитав стандарт я понял, что без поллитры не разберешься в логике template-ов (и вообще Страуструп похоже был сильно накурен или пьян, когда придумывал их синтаксис и правила).
PS: А вот чего тот код, который во втором посту, работает, и правда непонятно - там функция main объявлена синтаксически неверно
(hint: у всякой функции должен быть указан тип возврата и если он не void, то внутри должен происходить возврат значения соответствующего типа).

a10063

PS: А вот чего тот код, который в первом посту работает, и правда непонятно - там функция main объявлена синтаксически неверно
потому что компилятор умный
если очень хочется, можно предупреждения включить, он скажет
также всякие опции соответствия стандартам есть...

mira-bella

потому что компилятор умный
имхо наоборот, потому что компилятор глупый (или очень старый)

a10063

что ж плохого в том, что он угадывает то, что программист хотел написать?
это ж помарки..

mira-bella

что ж плохого в том, что он угадывает то, что программист хотел написать?
очевидно плохо то, что программист при этом не учится и не привыкает писать правильно.
Не говоря уже о том, что ничего компилятор в данном случае не угадывает, поскольку программист (новичок как правило) не написав тип возврата, обычно думает, что возвращать ничего не надо, а на самом деле возврат по умолчанию (K&R C) - тип int (у функции main кроме того обязан быть тип возврата int). А отсутствие самого возврата означает возврат мусора (который завалялся в регистре возврата (EAX обычно для ia32 архитектуры. Это все очень далеко от того, чтобы "угадывать мысли программиста".

a10063

то, что "правильно" - вещь субъективная
понятное дело, что стандарт согласовывали мыслящие люди, он - вещь очень и очень полезная, но не нужно на него молиться
к тому же, как я уже написал, можно легко решить такую "проблему" с помощью пары свичей
А отсутствие самого возврата означает возврат мусора (который завалялся в регистре возврата (EAX обычно для ia32 архитектуры. Это все очень далеко от того, чтобы "угадывать мысли программиста".

неужели ты и правда так думаешь?
по-твоему, создатели компилера не смогли ничего лучше придумать, чем сваливать туда мусор?

mira-bella

неужели ты и правда так думаешь?
по-твоему, создатели компилера не смогли ничего лучше придумать, чем сваливать туда мусор?
я не думаю, я это знаю совершенно точно (по поводу большинства компиляторов)
это довольно легко проверить - скомпилить в ассемблерный код (в MSVC с листингом)
компилер просто ничего не делает, если ты не написал возврат (никакой "мусор" он специально не "сваливает" туда, просто текущее значение возврата ничем специально не заполняется, что там было раньше, то и осталось после возврата).
Откуда компилятор может знать какой ты извращенец, может ты ассемблерной вставкой какой-нибудь заполнил регистр возврата нужным тебе значением, например. Еще у многих компиляторов значение последнего вычисленного выражения находится в области возврата.
В качестве "мусора" как я уже сказал выступает текущее значение регистра возврата (или вершина стека, если в регистр не помещается, или еще чего-нибудь зависящее от платформы)

mira-bella

то, что "правильно" - вещь субъективная
нет, какая программа на C++ правильная - это вещь не субъективная, а прописанная в стандарте C++ совершенно четко и однозначно (точнее там парочка других терминов вводится вместо "правильная программа").
Это как заявить: "сколько будет 2+2 - вещь субъективная".
C++ конечно в процессе разработки, но есть в нем совершенно четко устоявшиеся вещи и необходимость писать тип возврата при объявлении любой функции - одна из них.
понятное дело, что стандарт согласовывали мыслящие люди, он - вещь очень и очень полезная, но не нужно на него молиться
никто и не молится
Я лично стандарт C++ воспринимаю тем, чем он должен по идее являться - определением языка C++ (и не беда, что он до сих пор не реализован в наиболее популярных компиляторах, зато они к этому стремятся в меру своих убогих возможностей). Что же еще считать определением, если не стандарт? Тем более это касается тех частей стандарта, которые всеми реализованы и притом одинаково.
(А почитав про шаблоны я еще меньше стал уважать стандарт C++ чем раньше, хотя и раньше я был далеко не в восторге от него)
к тому же, как я уже написал, можно легко решить такую "проблему" с помощью пары свичей
эээ
какую проблему? каких свичей?
проблему с возвратом функции main?

a10063

компилер просто ничего не делает, если ты не написал возврат (никакой "мусор" он специально не "сваливает" туда, просто текущее значение возврата ничем специально не заполняется, что там было раньше, то и осталось после возврата).
Откуда компилятор может знать какой ты извращенец, может ты ассемблерной вставкой какой-нибудь заполнил регистр возврата нужным тебе значением, например. Еще у многих компиляторов значение последнего вычисленного выражения находится в области возврата.
В качестве "мусора" как я уже сказал выступает текущее значение регистра возврата (или вершина стека, если в регистр не помещается, или еще чего-нибудь зависящее от платформы)

я тебя понял, просто про "сваливает" некорректно выразился, признаю
вообще, с написанным согласен
нет, какая программа на C++ правильная - это вещь не субъективная, а прописанная в стандарте C++ совершенно четко и однозначно (точнее там парочка других терминов вводится вместо "правильная программа").
Это как заявить, что "сколько будет 2+2 - вещь понимаете ли субъективная".

в том контексте я понял "правильно" не в смысле "согласно стандарту"
Я лично стандарт C++ воспринимаю тем, чем он должен по идее являться - определением языка C++ (и не беда, что он до сих пор не реализован в наиболее популярных компиляторах, зато они к этому стремятся в меру своих убогих возможностей). Что же еще считать определением, если не стандарт? Тем более это касается тех частей стандарта, которые всеми реализованы и притом одинаково.

вообще, у популярных компилеров есть куча своих расширений. можешь считать отсутствие требования int у main таким расширением
эээ
какую проблему? каких свичей?
проблему с возвратом функции main?

с помощью свичей (switches) можно включать/отключать свойства компилятора, например решить "проблему" отсутствия строгого следования стандарту

mira-bella

вообще, у популярных компилеров есть куча своих расширений. можешь считать отсутствие требования int у main таким расширением
расширения бывают грубо говоря полезные, безобидные и вредные. Имхо это "расширение" (точнее рудимент "обратной совместимости" с K&R C) без сомнения вредное - оно не предоставляет никаких дополнительных возможностей, но сбивает с толку новичков (которые могут думать, что пустота значит void) и ухудшает читабельность кода.

a10063

но сбивает с толку новичков (которые могут думать, что пустота значит void)
не всех, а только тех, кто недочитал книжку
кстати, что такого страшного произойдет, если программа вернет мусор? ну шелл неправильно отреагирует, ну засуетится новичок наоборот, это его заставит книжки читать, а не решать проблемы методом проб и ошибок!

mira-bella

кстати, что такого страшного произойдет, если программа вернет мусор? ну шелл неправильно отреагирует, ну засуетится новичок
В том то и проблема, что не засуетится, поскольку с очень большой вероятностью не заметит. Нет сообщения об ошибке - нет проблем.
Обычно ничего страшного, если это функция main, а если значение другой какой-то функции будет считываться и обрабатываться, но не возвращаться из этой функции?
Короче даже в стандарте C90 такой синтаксис был устаревшим (может даже в C99 запретили наконец, хз а в C++ он был некорректным всегда.
В принципе логично, что если уж пишешь на C++, то и пиши на C++, а не на суржике каком-то. ИМХО.

a10063

В том то и проблема, что не засуетится, поскольку с очень большой вероятностью не заметит. Нет сообщения об ошибке - нет проблем.
если не заметит - тоже ничего плохого
ведь если ему не нужен код возврата из программы - то зачем ему навязывать?
Обычно ничего страшного, если это функция main, а если значение другой какой-то функции будет считываться и обрабатываться, но не возвращаться из этой функции?
эээ, нет! мы говорим о функции main. для другой функции код просто не откомпилируется.
да и вообще, ну укажет он int как возвращаемое значение, но ничего не вернет - с отключенными предупреждениями компилятор ему ничего не скажет - так что проблему мусора это не решает

a10063

кстати, хотел узнать, раз ты разбираешься в стандартах - какие считать текущими?
ISO/IEC 9899:1999 Programming Language C & ISO/IEC 14882:1998 Programming Language C++?

mira-bella

если не заметит - тоже ничего плохого
я же вполне конкретно сказал, что именно плохо, на мой взляд.
Что программист так и не узнает, что у ВСЕХ функций должен быть тип возврата. И что функция main - не какое-то "особое место" в програме, а обычная функция как и все остальные с таким же синтаксисом объявления (но с определенными требованиями к ее типу).
эээ, нет! мы говорим о функции main. для другой функции код просто не откомпилируется.
Тем в большей степени это подтверждает утверждение о тупости компилятора. Что за фигня, когда функции main позволено то, что не позволено другим функциям?

mira-bella

кстати, хотел узнать, раз ты разбираешься в стандартах - какие считать текущими?
ну очевидно текущими логично считать самые последние утвержденные стандарты.
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) последний

a10063

ok, я понял твою позицию; мое мнение - что функция main все-таки отличается от других функций, поэтому для меня неудивительно, что компилятор не ругается
вопрос о вредности является глубоко субъективным, поэтому предлагаю закончить флейм на эту тему
меня вот какой вопрос занимает: где в стандарте написано, что для функции обязательно описывать возвр. знач-е?
я полистал, трудно с непривычки его читать, вот нашел кое-что:
8.4 Function definitions [dcl.fct.def]
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. ]
не означает ли все это, что я мог не писать 'int' в примере (см. выделение жирным)?
не должно бы означать, но тогда что это означает?

mira-bella

не означает ли все это, что я мог не писать 'int' в примере (см. выделение жирным)?
Может и означать, если из других частей стандарта не следует обратное.
Я бы очень удивился, если бы узнал, что так и есть.
не должно бы означать, но тогда что это означает?
Как минимум это означает, что это определение синтаксиса определения всякой функции, в том числе конструктора, деструктора, оператора преобразования типа - а они объявляются без типа возврата.
Означает ли это, что тип возврата можно не писать в обычных функциях, хз. Если так то непонятно, почему компиляторы ругаются, и, насколько я помню, Страуструп заявляет обратное.
Оставить комментарий
Имя или ник:
Комментарий: