#c #templates #traits
#c #шаблоны #Трейты
Вопрос:
У меня есть эта функция шаблона:
template <class P>
double Determinant(const P amp; a, const P amp; b, const P amp; c) {
return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y);
}
но я не хочу постоянно указывать возвращаемый тип double
— P:: x и P:: y также могут быть целыми числами, и мне нужна эта функция в обеих ситуациях. Есть ли способ указать тип x и y, что-то вроде этого?
//doesn't compile; can't deduce template argument for T
template <typename T, class P>
T Determinant(const P amp; a, const P amp; b, const P amp; c) {
return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y);
}
редактировать: Мой компилятор — VC2005
правка 2: извините, что забыл упомянуть: К сожалению, я не могу изменить реализацию структур для P; одним из типов точек, с которыми я имею дело, является CPoint MFC / ATL, которые жестко запрограммированы как { long x; long y; }
.
Комментарии:
1. Какой компилятор (ы) вы используете?
2. vc2005 — теперь отредактировано в вопросе.
Ответ №1:
Компилятор не может определить возвращаемый тип шаблона функции из аргумента функции. Вывод типа выполняется только с помощью аргументов функции.
В C 03 вы можете определить typedef в своем классе как:
struct A //suppose A is going to be type argument to your function template
{
int x, y; //I'm assuming type of x and y is same!
typedef int value_type; //type of x and y!
};
И затем вам придется переписать свою функцию как:
template <class P>
typename P::value_type Determinant(const P amp; a, const P amp; b, const P amp; c) {
return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y);
}
Теперь обратите внимание на возвращаемый тип, это зависимый тип. Его :
typename P::value_type
Здесь требуется ключевое слово typename
.
Хорошо, как вы сказали, вы не можете изменять свои структуры, тогда вы можете использовать вместо них черты. Вот как это можно сделать:
template<typename T> struct PTraits;
//Suppose this is your type which you can't modify
struct A //A is going to be type argument to your function template
{
long x, y;
};
//specialization: defining traits for struct A
template<>
struct PTraits<A>
{
typedef long value_type; //since type of A::x and A::y is long!
};
И ваш шаблон функции будет выглядеть следующим образом:
template <class P>
typename PTraits<P>::value_type Determinant(const P amp; a, const P amp; b, const P amp; c) {
return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y);
}
Обратите внимание на возвращаемый тип; теперь он немного отличается:
typename PTraits<P>::value_type
Опять же, value_type
это зависимое имя, поэтому ключевое слово typename
является обязательным.
Обратите внимание, что вы должны специализироваться PTraits<>
на каждом типе, который вы передаете в шаблон функции, как это сделал я.
Комментарии:
1. отличный трюк, Наваз; не думал об этом раньше. К сожалению, я не могу изменить реализацию структуры; одним из типов точек, с которыми я имею дело, является CPoint MFC / ATL, которые жестко запрограммированы как
{ long x; long y; }
, без полезных определений типов такого рода.2. @martin_ljchan: Смотрите второе решение!
3.@martin_ljchan: Вы можете начать отсюда: Особенности: новая и полезная шаблонная техника Натана К. Майерса. Я думаю, что именно он первым описал эту технику. 🙂
Ответ №2:
Мне нравится использовать подход в стиле черт для этого:
template<typename T> struct DeterminantReturnInfo {};
template<> struct DeterminantReturnInfo<MyType> { typedef MyOtherType ReturnType; }
template< typename T >
typename DeterminantReturnInfo<T>::ReturnType Determinant( const P amp; a, const P amp; B, const P amp; c)
{
return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y);
}
Если вы хотите, чтобы по умолчанию он был равен double, тогда вы просто добавляете typedef double ReturnType;
к исходному шаблону.
Комментарии:
1.
typedef ReturnType double;
? Неправильный синтаксис2. Действительно. Исправлено. (Хотя это очевидная опечатка, которую вы могли бы исправить с помощью кнопки редактирования)
Ответ №3:
Если вы используете Visual Studio 2010 или GCC 4.5 , вы можете использовать форму завершающего типа возвращаемого значения:
template<class P>
auto fun(const Pamp; a) -> decltype(a.x a.y){
return a.x a.y;
}
Благодаря decltype
мы автоматически получаем правильный возвращаемый тип. Кроме того, вычисление по-прежнему выполняется только один раз в теле, а не в завершающем возврате.
Ответ №4:
Проверьте возвращаемый тип его переменной-члена типа x / y. Вы можете не возвращать so тип T.