template<> Вопрос для профессионалов С++.
#include <iostream>
using namespace std;
template <class T>
void get() {
cout << 2;
}
template<>
void get<char>() {
cout << 5;
}
template<>
void get<int>() {
cout << 4;
}
int main() {
get<char>();
get<int>();
get<double>();
return 0;
}
Предполагалось, что на экран выведется:
5
4
2
Но в реальности выводиться:
5
5
5
При этом, если поменять местами частные спецификации для char и int, т.е. записать:
template <class T>
void get() {
cout << 2;
}
template<> // местоположение
void get<int>() { // в коде изменено
cout << 4; //
} //
template<> // местоположение
void get<char>() { // в коде изменено
cout << 5; //
} //
int main() {
get<char>();
get<int>();
get<double>();
return 0;
}
то будет выводиться уже:
4
4
4
ПОЧЕМУ? Почему не работает, как предполагалось?
P.S. Компилятор из VS6.0
Пример кода достаточно бессмысленный т.к. использование шаблонов функций предполагает наличие в ее заголовке параметров, зависящих от типа T.
Например
template<class T> void get(T t) {...}
Именно по фактическим типам параметров заголовка компилятор узнает какую специализацию следует генерировать при вызове функции. Что делает компилятор в твоем случае, трудно сказать. Могу высказать версию, что указание специализации при вызове фукнкций из main он просто игнорирует, а вызов get() разрешает, выбрав первую попавшуюся частную специализацию.
Пример кода достаточно бессмысленный
Ну на счет бессмысленности с точки зрения программиста, это мне решать :о)
Мне такой код, при условии его работоспособности, упростил бы жизнь.
т.к. использование шаблонов функций предполагает наличие в ее заголовке параметров, зависящих от типа T.
Например
template<class T> void get(T t) {...}
Честно говоря, не нашел точного указания о том, что в заголовке должны быть "параметры зависящие от T", т.е. от параметра шаблона.
Именно по фактическим типам параметров заголовка компилятор узнает какую специализацию следует генерировать при вызове функции.
Не совсем так. MSDN "Working with Function Templates":
Explicit specification of the template arguments for a function template is allowed. For example:
template<class T> void f(T) {...}
void g(char j) {
f<int>(j); //generate the specialization f(int)
}
Что делает компилятор в твоем случае, трудно сказать. Могу высказать версию, что указание специализации при вызове фукнкций из main он просто игнорирует, а вызов get() разрешает, выбрав первую попавшуюся частную специализацию.
Видимо так. Но почему? Это соответствует стандарту языка или очередной "баг" (отличие от стандарта) от фирмы M$, как, например, отсутствие частичной спецификации шаблонов, инстанирование шаблона шаблоном (хотя в VC++.NET это уже есть) и т.п.?
Ну на счет бессмысленности с точки зрения программиста, это мне решать :о)
Мне такой код, при условии его работоспособности, упростил бы жизнь.
Решай сам конечно, я просто не понимаю чем может упростить жизнь подобный код: какая разница, написать
get<char>();
get<int>();
или
get_char();
get_int();
Откуда такая тяга к шаблонам, если можно обойтись без них ?
Не совсем так. MSDN "Working with Function Templates":
Explicit specification of the template arguments for a function template is allowed. For example:
template<class T> void f(T) {...}
void g(char j) {
f<int>(j); //generate the specialization f(int)
}
Ты не обратил внимания на эту строчку ?
template<class T> void f(T) {...}
Это как раз то, о чем я писал.
А явное указание используется для того чтобы преобразовать j из char в int. С таким же успехом можно было написать.
f((int)j)
вместо
f<int>(j)
Да, VC 6 не блещет поддержкой шаблонов, но в данном случае его поведение вполне разумно.
Решай сам конечно, я просто не понимаю чем может упростить жизнь подобный код: какая разница, написать
get<char>();
get<int>();
или
get_char();
get_int();
Откуда такая тяга к шаблонам, если можно обойтись без них ?
Это был лишь пример кода. Без контекста, видимо, действительно трудно понять необходимость и упрощение при использовании шаблонов, но если ты знаком с паттерном "стратегия", тогда преимущество очевидно.
Ты не обратил внимания на эту строчку ?
template<class T> void f(T) {...}
Это как раз то, о чем я писал.
А явное указание используется для того чтобы преобразовать j из char в int. С таким же успехом можно было написать.
f((int)j)
вместо
f<int>(j)
Да, VC 6 не блещет поддержкой шаблонов, но в данном случае его поведение вполне разумно.
Нет. Не разумно. В более продвинутых компиляторах, например в VS.NET 2003, мой пример корректно работает. Т.о. моя проблема - это баг VC++6.0
Это был лишь пример кода. Без контекста, видимо, действительно трудно понять необходимость и упрощение при использовании шаблонов, но если ты знаком с паттерном "стратегия", тогда преимущество очевидно.
Не знаю, что за "стратегия" такая. Просвети, если не трудно.
Контекст здесь не причем. Смысл использования шаблонов с частными специализациями в том, что компилятор сам выбирает нужную реализацию в зависимости от типа аргументов. А ты выбираешь ее ручками. По смыслу это тоже самое, что создать несколько функций с разными именами. Поэтому я и написал, что пример бессмысленный в части применения шаблонов (и в чем я до сих по уверен :) ).
Чтобы перевести эту бесплодную дискуссию в конструктивное русло могу предложить изменить заголовок вот так:
template<class T> void get(T* = NULL) {...};
Думаю будет работать.
Есть такой код:
#include <iostream>
using namespace std;
template <class T>
void get() {
cout << 2;
}
template<>
void get<char>() {
cout << 5;
}
template<>
void get<int>() {
cout << 4;
}
int main() {
get<char>();
get<int>();
get<double>();
return 0;
}
Немного не правильное использование шаблона функции. Такое использование правильно для шаблона класса. Для функции же правильно передача шаблона через фактический параметр, а не через <>:
template <class T>
void get(T i) {
....
}
int main() {
char a;
get(a);
int b
get(b);
double c
get(с);
return 0;
}
Да и еще одно - из свойств шаблона функций:
Список параметров шаблона функции не может быть пустым, т.к. при этом теряется возможность параметризации и шаблон функции становится обычным определением конкретной функции.
А вообще твой пример несколько не понятен, т.е. ты в принципе можешь воспользоваться перегрузкой функций.
это всё баг VC++6.0, в .NET 2003 все отлично работает!
Немного не правильное использование шаблона функции. Такое использование правильно для шаблона класса. Для функции же правильно передача шаблона через фактический параметр, а не через <>:
Где такое сказанно? В спецификации языка этого нет.
Список параметров шаблона функции не может быть пустым, т.к. при этом теряется возможность параметризации и шаблон функции становится обычным определением конкретной функции.
Список параметров шаблона или список аргументов функции?
А вообще твой пример несколько не понятен, т.е. ты в принципе можешь воспользоваться перегрузкой функций.
Поверьте, я знаю, что такое перегрузака. :о)
А вы знаете, что такое паттерны и в частности паттерн "стратегия"?
Советую почитать на эту тему.
http://ooad.asf.ru/patterns/
Наконец-то кто-то меня поддержал, я уж было засомневался.
Green
Спасибо за ссылочку. Почитал про "стратегию". Стандартное и довольно очевидное решение.
"Стратегия" обычно реализуется через наследование, хотя никто не запрещает использовать шаблоны. В этом случае контекст является собственно шаблоном, а класс-стратегия - его фактический параметр. Именно так, кстати, устроен стандартный класс string.
Как твой пример связан со стратегией я так и не понял.
В современных компиляторах очень много возможностей, которые не стоит использовать при правильном проектировании (ИМХО).
Спасибо за ссылочку. Почитал про "стратегию". Стандартное и довольно очевидное решение.
"Стратегия" обычно реализуется через наследование, хотя никто не запрещает использовать шаблоны. В этом случае контекст является собственно шаблоном, а класс-стратегия - его фактический параметр. Именно так, кстати, устроен стандартный класс string.
Как твой пример связан со стратегией я так и не понял.
Про использование шаблонов при реализации "стратегии" ты всё понял правильно. Честно говоря, не вижу преимуществ у наследования перед использованием шаблонов, т.к. в большинстве случаев придется использовать множественное наследование, что само по себе не есть хорощо. Почему, думаю, очевидно.
Мой пример связан со стратегией след. образом. Есть класс "документ", есть класс "вид", т.е. обычная концепция Doc/View. Способ заполнения вида и выбор информации из документа могут варироваться в различных проектных решениях. Т.о. целесообразно введение стратегии заполнения. При этом выбор иформации из документа производиться по индексу, передаваемому в стратегию, как праметр шаблона. Т.о. класс документа мог бы выглядеть примерно так:
class Human {
private:
int age;
int weight;
char* name;
public:
char* get(int index)
{
switch(i) {
case 0: return name;
case 1:
........
default:
........
}
}
};
Но в данном случае switch - это лишний код и лишние операции, т.к. ещё на этапе компиляции будет известно какие из его веток будут работать, а какие - никогда не будут пройдены.
Т.о. я решил "реализовать" этот switch еще на этапе компиляции при помощи шаблонных функций:
template<int T> char* get() // то же что и default
{
......
}
template<> char* get<0>() // то же что и case 0
{
return name;
}
template<> char* get<1>() // то же что и case 1
{
......
}
и т.д.
Т.о. код, который не используется, не будет присутствовать в скомпилированном объектном файле, и нет передачи аргументов при вызове функций.
Т.о. switch вроде бы есть, а вроде и нет.
Как видите, ни перегрузка ни использование нескольких функций с разными именами здесь не подходит.
Я не спрашиваю, будет ли это работать, хорошо ли делать так или нет. Я знаю, что это работает потому, что это уже работает. И этот подход вписывается в концепцию всего проекта и приводит к ощутимым положительным результатам как по гибкости проектирования, так и по быстродействию.
Первоначальный вопрос был про нежелание такого кода работать в VC++6.0, потом выяснилось, что это особенность компилятора, а не языка.
В современных компиляторах очень много возможностей, которые не стоит использовать при правильном проектировании (ИМХО).
Звучит примерно так:
"Мы боимся всего нового, поэтому и Вам советуем действовать постаринке."
"Правильное проектирование" - весьма субъективное понятие.